Unit Test Frontend Components

Understanding what should we test

José Silva
Oct 16 · 6 min read
Photo by Mark Oates from Burst

Testing our code is (or should be) an integral part of our daily work. It brings us more confidence, more quality, and better documentation.

With a good percentage of coverage, everyone has less fear to change code, and consequently, it leads to more happiness.

Photo by Pixabay on Pexels

The first question we ask ourselves when we start writing unit tests is, “What should I test?”. Answering wrongly to this question can result in too much or too few tests. We only need the right amount!

Let’s start with the basics.

Unit Test

Unit testing consists in isolating part of our code, like a function, call it with some inputs and verify if it returns the expected result.

What should we test in this function?

function sum (a, b) {
return a + b
}

Unit Test Frontend Components

For frontend components, the concept is the same, but instead of functions, our unit is the component.

An excellent explanation can be found on Vue Test Utils documentation:

For UI components, we don’t recommend aiming for complete line-based coverage, because it leads to too much focus on the internal implementation details of the components and could result in brittle tests.

Instead, we recommend writing tests that assert your component’s public interface, and treat its internals as a black box.

For example, for the Counter component which increments a display counter by 1 each time a button is clicked, its test case would simulate the click and assert that the rendered output has increased by 1. The test doesn't care about how the Counter increments the value, it only cares about the input and the output.

The benefit of this approach is that as long as your component’s public interface remains the same, your tests will pass no matter how the component’s internal implementation changes over time.

It is also important to highlight that:


Inputs & Outputs

Inputs are everything that can result in a side effect (output) on a component.

Some of the more frequent inputs and outputs of a component

The most simple inputs to identify are props and user interactions. Almost every component has at least one of these inputs.

As we increase the complexity of a component, other kinds of inputs start appearing. Data resultant from a store (Redux, Vuex), API responses or router information are very common ones.

Most components end up with other components inside, and when a child component emits an event, it is considered input on the parent component, if it reacts to it.

All these inputs cause some side effect on our component. It can be the render of a label (DOM), an event being sent, a store or an API being called, data being sent thought props to a child component or even our URL being modified (Route).


Documentation

Once we have a good percentage of coverage, tests are the most detailed documentation we can have. However, it is easy to end up with test files that are hard to read or to find anything.

To avoid that mess, a good structure is necessary:

Example inspired on Matt O’Connell’s excellent presentation

Following a strategy like this, test descriptions become shorter, and it is easier to find a specific test or to check what still needs to be tested.

In the example, I use describe as an aggregator of type of inputs and the it always starts specifying the input being tested.

Practical example

Now that we introduced what should we test and how should we structure our test files, let’s try to apply it to a concrete example.

In this example, we have two components, UsersList and UsersListRow. To test them, we do the question: What does each component really do? Let’s isolate each, visually, to quickly answer.

UsersListRow

UsersListRow component

What does it do?

UsersListRow Inputs & Outputs Diagram

UsersList

In the following image, the content of the UsersListRow is empty because it does not matter. We need to focus on what the UsersList component does and to that, we only need to know the public API of the UsersListRow component.

UsersList component

What does it do?

UsersList Inputs & Outputs Diagram

Once we specify what each component does, we can efficiently structure our test files.

Not going outside the component scope, allow us to refactor the internals of UsersListRow without having consequences on the UsersList tests.

Note

Changing UsersListRow to emit a ‘remove’ instead of a ‘delete’ event would break the unit tests of the UsersListRow but not the UsersList tests. Integration/Functional tests are the ones responsible for catching this problem.

Conclusion

With the right mindset, writing unit tests can be enjoyable and lead to higher quality and more confidence in our work.

A good organization of our tests can result in better documentation and can help us to understand what is already tested and to navigate through them quickly.

To answer the question “What should I test?” we must be able to answer the question “What does the component do?”.


Thanks for reading. You can learn more about me on Medium, Github and Linkedin.

The Startup

Medium's largest active publication, followed by +526K people. Follow to join our community.

Thanks to João Teixeira

José Silva

Written by

Lead Frontend Developer @ JumiaPay, Vue.js enthusiastic.

The Startup

Medium's largest active publication, followed by +526K people. Follow to join our community.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade