Outline

This is from a tweet which you can find here.

Previous Posts:
  1. Mock Types,
  2. The AAA Structure
  3. Interaction vs State Testing
  4. DAMP vs DRY

Thinking From the Client's Perspective is one of the pieces of unit testing that is often overlooked and many people never realize or practice.

When you write a unit test for a unit of your code (again, I'll use SUT when referring to the code under test), that unit test interfaces with and talks to your code. That makes the unit test a client of your code. This is important.

You now have two different clients for your code at a minimum: 1. the code in your production system that uses the SUT - (there may be multiple places but there's at least 1, otherwise why would you write the code?) 2. The unit test

The act of writing the unit test means that you're writing a client for your code/SUT so you get to once again think about how to call the SUT. I want my SUT to do X. What methods exist? Which do I call? In which order? What are those methods like? What is the signature?

All of these are opportunities to look at the "usability" of your SUT. Meaning, how easy is it to get the SUT to do its job? Let's look at an over-exaggerated example. Our own custom addition method:

interface Sign {
  positive: boolean;
  negative: boolean;
}
interface Operand {
  value: number;
  mantissa: number;
  sign: Sign
}

function add(augend: Operand, addend: Operand) {
  // implementation
}

and here's the code to use our add function:

// 2.5
let num1 = {
  value: 2;
  mantissa: 0.5;
  sign: { positive: true; negative: false; }
}

// 3.5
let num2 = {
  value: 3;
  mantissa: 0.5;
  sign: { positive: true; negative: false; }
}

let result = add(num1, num2);
// should be 6

look at how hard it is to do a basic operation! This may be exaggerated, but the same principle applies to all code. Think about how the consumer uses the code. Let's look at a slightly less exaggerated example:


function add(operands: number[]) {
  // loop through the array and add the numbers 
}

let result = add([num1, num2]);

This is not as bad, but still tough. You have to wrap everything in an array, and if that's extra work, then the client code gets some additional burden. Here's an implementation that really thinks about things from the client's perspective and optimizes for that:

function add(...operands: number[]) {
  // loop through the array and add the numbers 
}

let result = add(num1, num2);

Now few people would code up something like the first example. But if you look through your code you've written over the last 6 months, you will likely find places where thinking from purely the client's perspective may cause you to refactor some of your methods.

This is one of the huge pieces of value that unit tests provide. They force you to look at your SUT from the client's perspective at least one more time. At that point do you take advantage of the opportunity and maybe do some refactoring? If not, you're missing out.

As you refactor the SUT, and its clients, you lower the cognitive load you have to bear tomorrow when you have to support that code.

Now this benefit is greatly enhanced when you practice TDD since you won't have even written the SUT yet so you don't even have to refactor. And of course you can just do this automatically right from the start when coding anything, but nobody is perfect.

Even if you train yourself to think from the client's perspective, as you code up a unit, generally your mind sits INSIDE the unit, and you naturally optimize the interface to make the job of the inside code easier, not the client. This is almost always less effective.

So write your unit tests, and as you do, think about how you use the SUT, and if you can see a better way to call the code, refactor. It's better done now than later.

Discounted Courses

Here at Thinkster, we use educational science to teach you five times faster than you would learn the same topics elsewhere. By joining as a Pro subscriber you have access to our complete library on courses on Angular, React, Vue, JavaScript, and More.

To help you get started, use this link for a discount on a monthly membership.

Or save even more with an annual membership.

 

I finished! On to the next chapter