Testing your code is always a good practice. However this can easily become time consuming to write tests and maintaining it.
In this article we will interest ourselves to a tool that will help us to reduce the code we are writing when we are creating tests.
Fixtures
The feature in question is named fixture.
This consist of templates files containing configurations for the test that will be loaded each test giving it a different context.
This allowing a same test to be reused multiples times without having to rewrite the code but instead just by swapping the context we map to this test.
Advantages
The advantage of this approach is obliviously the reduction of code and of duplication of it inside the testing folders.
Instead of having to write 3 tests for 3 different cases, we can instead create one test with 3 different contexts changing only the values we inject into the tested code and the expectation concerning its result.
This method also allow us to have a better organization of our code as we can now have only one test case per file without having numerous files inside one same folder.
Drawbacks
However the main drawback of this method is that we add complexity inside the test logic that can lead to reduce the speed we are writing one test but as the code is done to be mainly read and maintained this is not a big issue.
Another drawback is that we will have to refactor your old fixtures from a tests when we will have to add new logic in a existing function as the tests are not totally isolated.
Implementation
Now that we learnt what are fixtures we will now see a way to implement the feature inside a Jest testing project if you want to check directly the code it is available directly on this repository:
https://github.com/CrochetFeve0251/fixture-example
The first step will be to create a folder tests inside your project to add your test files.
Creating a state
The first thing we will have to create is a state to contain the context of each test for that we gonna create a file with the name state.js and we will add the following content inside it:
const state = {
state: {}
};const getTestState = () => state.state;const setTestState = new_state => {
state.state = new_state;
};module.exports = {
getTestState,
setTestState,
}
This code create a state that we will reuse later to save the context of the test but also 2 accessors to get and set the value of this state.
Mapping the context to the test
Then the next step will be to create a function that will contain the logic mapping the test to its context.
For that we will create a file tests.js inside the tests folder and add this content to it:
const {setTestState} = require("./state");module.exports = (fixture, callback = () => {}) => {
const fixtures = require(fixture);fixtures.map(item => {beforeEach(() => {
const pattern = item.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const regex = new RegExp(`.*${pattern}$`);
if(regex.test(expect.getState().currentTestName)) {
setTestState({ config: item.config, expected: item.expected });
}
})it(item.name, callback);afterAll(() => {
setTestState({});
})
});
}
Inside this file we do multiple things:
First we fetch the data from the fixture file by requiring it.
const fixtures = require(fixture);
Then we iterate on each context inside of the fixture file and we add 3 things:
- A beforeEach callback that will check if the current test is the right one and if it the case add data to the state.
- The test by itself.
- A afterAll callback that will clean the state.
beforeEach(() => {
const pattern = item.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const regex = new RegExp(`.*${pattern}$`);
if(regex.test(expect.getState().currentTestName)) {
setTestState({ config: item.config, expected: item.expected });
}
})it(item.name, callback);afterAll(() => {
setTestState({});
})
Once we did that the main logic is now set and we will just have to add tests and fixtures.
Add tests and fixtures
The first thing we will have to do now is to add some dummy code to test.
For this example that gonna be the function sum that will have the following logic and will be in the file src/sum.js:
function sum(a, b) {
return a + b;
}
module.exports = sum;
Then to keep your tests clean we will create two folders in your tests folder to separate fixtures from unit tests:
- Fixtures: For your fixtures.
- Unit: For your unit tests.
Inside this two folders we will add a sum.js file.
For the one in the Fixtures folder we will add the following content:
module.exports = [
{
name: "When 1 + 1 should return 2",
configs: {
value1: 1,
value2: 2,
},
expected: 2
},
{
name: "When 0 + 0 should return 0",
configs: {
value1: 0,
value2: 0,
},
expected: 0
}
];
For the one in the Unit folder we will add the following content:
const tests = require('../tests');
const {getTestState} = require("../state");tests('../../Fixtures/sum', () => {
const { configs, expected } = getTestState();
expect(sum(configs.value1, configs.value2)).toBe(expected);
});
Once we did that, voila our tests with fixtures are ready to be used. I hope you enjoyed this tutorial and that you will be using fixtures for your future projects.
Leave a Reply