Reducing magic boilerplate and making your React unit tests a lot more concise

Because sometimes less is more — my everyday endeavour to refactor relentlessly.

As a software engineer, we write a lot of tests every single day — it’s one of the many ways we protect what we ship from ourselves and from other engineers.

The more tests we write and encounter the more Magic we see happening in these tests. Sometimes we’ll ask ourselves— what just happened in this test? Magic occurs when we’re reading a spec and suddenly we have no idea where it got a specific value or something — tl;dr things just happened in that test’s scope.

Say no to Magic boilerplate.

Here are the following requirements — if you’re not using these tools then you will probably have a different pattern but most of the time they are the same.

Mocha (testing framework)

Code (assertion library)

Enzyme (testing utility for React)

Let’s assume for a second that this was the component that we’ve written tests for…

Judging from the code it’s a component that displays the taxable income and it has a label and value and the value is displayed in US dollars. In order for this component to render properly, a taxableIncome prop of type number is required.

What does our test look like?

It all started out with us writing a test for the section. Why? Because that’s the first thing we need, then the test for the label and eventually the value being displayed. BDD done!

Let’s focus particularly on line’s 40 to 49 real quick. We can see that we’ve repetitively called props() 4 times. What a waste of time and effort because all we really want to know is — in order to display the taxable income we need the following to be true.

It is FormattedNumber component and this component is a children of the current component that we’re rendering. We should be using the following props in order for us to get the rendered HTML we want.

Instead of expecting 4 times, why not expect it once and be done with it? In order for requirement #2 to happen, all these props have to be there all at once. In short we should be testing them as a group.

Behold our first refactor iteration!

One expect to solve our problem! We can do this because Enzyme’s props() method returns an object. We can then do a deep comparison with our expectedTaxableIncomeProps [Yay naming convention!] If you’re not seeing .deep.equal it’s because we’re using version 3.x of code which automatically does a deep comparison.

Still not happy with this result.

You’re probably asking yourself (and me), well Rodo where the hell does mockProps come from anyway? How did it find its way in our atomic test? [Magic!]

With mocha we typically use beforeEach() to set things up to run before each test. I personally used to be a fan of these helper methods but they are often abused in a larger code base with multiple contributors and it can become cumbersome to maintain let alone look up if you’re wondering where stuff came from.

Let’s get rid of that magic and make sure that the next person who looks at this piece of art knows exactly what is going on and we’ll save them the time to find out where things are coming from.

Here is our old test setup with beforeEach() .

And here is the new one.

Let’s break this down real quick.

Instead of just assigning values to component and mockProps we wrote two methods that we can call to generate the required props with requiredProps() and to render the component with render() .

Why requiredProps() ? We call it required props because this is the props that is required for our component to work — this is our bare minimum.

Do we override our required props? No we don’t. We do however pass a new props argument in render() if we want new props. In fact we should not even care about the values being returned when calling requiredProps() because they are simply there to make it work. They are not there for asserting if the prop value is properly being set.

How does our test look like now?

Boom shakalaka! Let’s go over this refactored test in detail so we know what is going on in our atomic test.

We now have a prop object that has our requiredProps() and the taxableIncome value that we are expecting for our test. Oh but Rodo! requiredProps() also returns a taxableIncome value that you can expect later! Y U MEK 1MORE?! The answer is pretty simple, for this test, we want to make sure that the taxableIncome value that we’re generating originated from this test and not some random method. Remember what we said about requiredProps() values not being used for assertion? It’s for real, dawg. It really is there so that you do not get any prop warnings.

We then render() the component and pass the props we’ve declared at line 3. Just like what we mentioned in #1, we want to make sure that for this test the props that we’re sending are originating from this test alone.

And that’s it! We reduced the amount of expectations and our test is a lot more concise now.

Final gist for you folks!