home

Blog

Unit tests: what you need to know before getting started

Blog, Pitang.

João Victor Oliveira

Unit tests: what you need to know before getting started

Career

I will share some tips and reflections I have had so far working as a software developer on a project where unit tests are mandatory. Tips that seem obvious, tips that were given to me, and tips I picked up with practice. 


I currently work as a front-end WEB developer. I am part of a development project in React and for tests we use React Testing Library alongside Jest. 


Tests are living documentation, it is laborious to maintain tests, they must change along with business rules, they need maintenance, and that is why they constantly break. 


Probably every developer will spend several hours writing or adjusting a test. This magnitude is proportional to the size of the project; the larger the project, the more tests there will be to write and maintain. There is no right or wrong amount. 


We want to spend more time early in development writing tests than later on, having to investigate/fix features that used to work properly and broke in a way that nobody understands at a time nobody can tell. 


In the project, when we started placing more importance on tests, I could feel that we reduced our rework with bugs in already-delivered features. I saw, in practice, their importance, so many times that I ran the tests and saw that my code change had an impact in places I wouldn't have imagined. 

1. Tests must be independent 

 

Pay attention to writing tests that pass at all times, no matter the circumstances. Whether it runs alone, runs before a test X, after a test X, or happens to be without internet. 


If the test requires the use of a mock or passing a parameter, make sure that variable or state is properly prepared for the expected behavior in the test. 


I have been through moments where I needed to use jest.clearMock (a Jest function) to clear the calls to my function. And I have messed up with JS object references when making clones (Deep clone and Shallow clone). 


We don't want to write tests that are passing due to scenarios that happened in other tests. 

2. High coverage should not be synonymous with security 

 

In my current project tests are mandatory, and not only that, we must cover at least 70% of the code. For many people (especially for non-developers), high coverage is to ensure predictability and security, but it's not necessarily like that. 


Tests will only do what you programmed them to do, so do something truly useful. Think more about the scenario and less about the lines of code that remain to be covered.

 

For example: I work with React. Imagine I have a component A, and that A has components B and C as children. If in my test I render A, then consequently I also render B and C. 


Because of rendering A, the test will already give coverage to code snippets in A, B, and C. Since my test for A should focus on A, I'm not actually testing B and C as I should. But the coverage went up! 


The tip I give in these cases is: always test components starting from the innermost and finishing with the outermost. 

3. Get used to breaking the test (on purpose) as you build it 

 

To make sure it is exactly that point we are testing and that we have total control of the test, we can simulate cases where the test breaks as we build the test itself. 


We familiarize ourselves with the test error so we can then fix it and make sure all scenarios are covered. 


It helps us find our way and also helps us better understand the component. Can you imagine if by chance the test doesn't break where we expected? A sign that we didn't correctly understand the workings of what we are testing. 


We can tweak both the test and the code being tested. And obviously, we fix it right away. 

4. Mock and test responsibility 

 

Mocks are objects created in a controlled way to mimic real objects. Because of them, we can simulate any behavior and focus exclusively on our test scenario. Test the if, the else, the edge cases, and much more. 


With all this power in hand we can lose ourselves, so keep in mind that a well-written test should have a single purpose. 


Before creating the test, we should already know what we expect from it. And while writing it, the test should feature a self-explanatory title, should be simple, lean, and test only one scenario (each test will have its responsibility). 


Mocks make us test far beyond the happy path. Each scenario will need a different test and possibly a different mock. 

5. Write testable code and test 

 

Here I could even talk about TDD, but that's not the goal of this post. It's not wrong to write tests after implementing, what's worse would be not writing the test at all. 


A good time to write tests (if you haven't done so) is right after you finish developing the feature. At that moment we have everything we need, because we know exactly how the code was made, what the expected behavior is, and the business rule is still fresh in memory. 


Don't write tests simply to make your component pass. Rather, make components that pass the tests. Don't be ashamed if you need to go back in development and touch some piece of code; sometimes we need to change how we code the component to better test it. Testing will, indeed, influence some decisions we make during development. 

6. In the end, you want to see your test break 

 

Tests were made to be broken! 


When you write a test, you are stating: if such a thing stops having this exact behavior, I want to be notified that this has changed. 


And then, if the test breaks in the future, it's up to the developers to understand the error and decide whether the adjustment should be made in the test or in the component; it will depend on the business rule. 

Recife

Cais do Apolo Street, 222 - 13th floor

ZIP: 50030-220 - Old Recife Recife/PE

Tel: +55(81) 3134 5200

Fax: +55(81) 3134 5374

São Paulo

Joaquim Floriano Street, 466 - Suite 1001 - 0

Brascan Century Building

Corporate, Itaim Bibi

São Paulo/SP

Recife

Cais do Apolo Street, 222 - 13th floor

ZIP: 50030-220 - Old Recife Recife/PE

Tel: +55(81) 3134 5200

Fax: +55(81) 3134 5374

São Paulo

Joaquim Floriano Street, 466 - Suite 1001 - 0

Brascan Century Building

Corporate, Itaim Bibi

São Paulo/SP

Recife

Cais do Apolo Street, 222 - 13th floor

ZIP: 50030-220 - Old Recife Recife/PE

Tel: +55(81) 3134 5200

Fax: +55(81) 3134 5374

São Paulo

Joaquim Floriano Street, 466 - Suite 1001 - 0

Brascan Century Building

Corporate, Itaim Bibi

São Paulo/SP