Photo by The Roaming Platypus on Unsplash

Make more human-readeable test

Many times, when I take a legacy code, I feel so glad to see that the code has a very high level of test-coverage, and it fails rarely. So at this point I feel like that I’m in a green field project and I strongly believe that I can maintain this code and add more features…

BUT WAIT!!

For my surprise, when I open the first test file… I feel desperate when I discover that I have been reading code for a long period of time, trying to understand complex structures and loosing my focus about the test approach or forgetting what feature I want to implement.

This situation might feel very similar for you. In fact, you don’t even need legacy code in order to live this experience; this could happen with your own code, so we will undertake a few tips where we can see how to improve our test readability and not lose focus.

In the first place, we will have a React Function Component that displays an specific message depending on a property value:

So when we take a look at this component we know that we will receive an exercise param on props, but we didn’t know anything about the structure (this is good), only that it has two fields: one that is called tags for being iterated, and a second one with is a warning message that only should be displayed when it’s different from null or undefined. Easy, right?

But when we enter to the test-file:

This works fine, it gives us a 100% coverage of all possible bifurcations of our component… but it’s really difficult to read (imagine that those 20 commented lines are really in the file). We would have a file that is 108 lines of code long for only testing 3 functionalities.

I personally understand the author of this code because your exercise value changes in each test, and using a let or override values for each one probably is not a good idea.

On the other hand, the tests names are should display the warning and should not display the warning but in a simple sight, I don’t know what is the condition for this to happen.

Finally, I think that when we said should display a list of two elements it was near to be correct because not using a specification doesn’t define that the data is an array and doesn’t indicate that the displayed component is a Chip either. Nevertheless we don’t want to know if it’s exactly two or more elements because it can make the developer believe that is a special condition of our domain.

Let’s start renaming the test to declare our intentions:

I based the test naming process on the given-when-then cycle. In my opinion this is a better approach of test naming because we can specify whether displaying the warning message or not. In most cases there is no need to actually specify this structure but that’s the logic behind it.

Finally, I remove the specification of 2 tags because this probably could have been written for one of these two reasons:

  • An old domain specification that is not present in the component anymore.
  • A mistake of the developer trying to make the test more specific.

Now we will try to hide structures from our test body. Depending on the language we are using, we could use some different techniques. In most cases, a function with dynamic params would be used, but extending the real class with a Mock Class in order to set default values in the constructor — unless specified — is a valid solution as well. In other words, we need a dynamic builder.

If we are planning on using TypeScript, we will rely on destructuring of JavaScript objects:

As we can see in the code, we can generate an object with the default values, or only set the values that we need for our test. This is a good thing for me: if I need to add, remove or modify the Exercise structure, I only have to pass those values to my builder, and my tests wouldn’t know about data structures.

Now, we only need to import this function an refactor out product definitions:

At this point, we are focused on the code of our tests and can detect other improvements that with 77 lines more we weren’t able to detect.

If we are familiar to use tests, the first thing that is in our mind is the idea of using beforeEach or beforeAll statements to define our wrapper because this actions are executed each time. Keeping with that idea we would lead us to depend directly on the props that the component receive at the moment of its construction, and that’s not what we’re looking for.

However, this doesn’t stop us to, at least, create a little function helper for creating our wrapper and returning our common part of the warning message:

Last but not least, we have two remaining lines of code, but the body of our test is more simple and it represents exactly what we need to know about the test and the component functionality. Additionally, this makes it really easy for us to add new features.

I sincerely appreciate you spent your time reading my article, I’m glad to share this information with other people in order to improve the actual way of testing our applications.

I would be thankful if you could share this with other people to give other vision of how we can keep improving our work as developers every day.

You can find me on my social networks:

I have to give a special thanks to Ana Cáceres 💜 and Mireia Scholz 💛 for helping me to translate this article!! 😍

Lean Mind

Lean Mind Blog