JEST-EACH: Writing maintainable tests

Anil Namde
Frontend Weekly
Published in
3 min readSep 6, 2018

--

One of the main problems on a sizable team with decently sized code is to maintain tests for the ever-growing codebase. Thanks to awesome frameworks anyone can write tests easily. Based on the team need it could be a simple set of Snapshot tests, Unit tests with an enzyme or Could also be integration tests checking some workflows spanning across the components.

General guidelines to write maintainable tests

But true challenge is to be able to write maintainable tests. Following are some of the common guidelines which really helps,

1. Understanding of code under test

A unit test is white box testing. One should invest time in understanding what we are testing and finding possible test cases positive and negative. Once it's clear that overall increase the understanding of the code itself.

function add(one, two){ return one + two; }
  • positive cases: a number is a positive fraction, negative fraction, positive number, negative number, 0
  • negative cases: a number is NaN, undefined, string, boolean, array, object, date …

2. Write a test that really matters not everything

It’s tempting to write tests for all the cases but its, not necessary. Based on the time available and the value of tests we can focus on the ones which are really important and have an impact. So in simple words, if100 lines of code have 200 lines of tests think again if its all needed.

// example for addition considered tests for integers only
expect(add(2, 3)).toBe(5)
expect(add(-2, 3)).toBe(1)
expect(add(-2, 0)).toBe(1)

3. Isolation of the tests

While writing tests ensure that unit test is not having a dependency on other global blocks or global variables. That ensures if you need to remove any tests in future it does not affect the others in the suit

4. When around some code keep visiting its tests

While working on a story ensure to visit tests written around code. If there is any scope of improving the tests take it as an opportunity. It's also necessary to realize that tests are nothing but code and deserve attention like actual feature code.

5. Avoid deep nesting

I am sure you have seen code like this in tests files quite often. It’s so painful to maintain tests like these as its hard to keep track of what's initialized in before sections and what is state of data when actual scenario gets exected.

describe('scenario 1 ', ()=>{
before(()=>{
...
});
after(()=>{
...
});
describe('sub scenario', ()=>{
before(()=>{
...
});
after(()=>{
...
});
describe('deep sub scenario', ()=>{
....
});
});
});

The best way is to avoid this nesting and try to isolate these scenarios which makes it really clean

describe('scenario 1', ()=>{
before(()=>{...})
after(()=>{...})
it('test scenario', ()=>{})
})
describe('scenario n', ()=>{
before(()=>{...})
after(()=>{...})
it('test scenario', ()=>{})
}

6. Test bucketing

As per the time and need project can have snapshot tests, unit tests, integration tests. Many time it happens that the team keeps all the code in a single file and eventually it becomes difficult to keep track of it. So its good practice to keep them separate for easy maintenance.

component.snapshot.test.js
component.unit.test.js
component.integration.test.js

Test refactoring example

There are different ways in which tests can be made more reliable and maintainable. Like can be achieved from refactoring code in nice way to splitting tests in files etc etc. But here I would like to share a simple example to demonstrate how it can be done by treating test as code.

So consider we wanted to test following function (d)=> 2*d

So as first pass most of the times we find tests written as below. Where scenarios are grouped and each assertion is kept in separate test block.

However its all javascript code end of day. So we can make it better by small refactoring to add test inputs in collection and iterate it.

Ok much better but still it can be improved

JEST-EACH to rescue

jest-each is now available in jest (version 23 onwards) itself as describe.each & test.each makes it even more easy to combine and execute the unit tests.

In case you wanted to use the template form that is also available

--

--