Before this internship I’ve never written tests for my code. I didn’t think it was necessary because if something doesn’t work in your code it either crushes and can see errors in JS console or you don’t get expected behavior or result. But in the Add-on Linter project I’m working on now having a test suite is really important:
- First of all on this project is working a group of developers and there is always a fear of breaking a code by making changes or adding new features to the code that you didn’t write or did write but forgot all it’s quirks.
- Also writing tests and thinking about all possible scenarios might pinpoint problems in a code and fix them before going into production phase.
- And last but not least with tests, you are more able to change code throughout the lifetime of an application. Tests provide a safety net, allowing you to refactor at any time without fear of breaking existing code, so you can add new methods, features or syntactic sugar.
For a function sum:
Jest test looks something like this:
Here, we’re simply testing that
1 + 2 equals
3. Let’s walk through this syntax:
- Each test begins with Jest’s built-in
test()always includes an string argument describing what we’re testing for. Above, we’re testing that function sum “adds 1+2 to equal 3”. Writing good, clear, descriptive explanations of what we’re testing is important.
- In the
expect()line, we detail what we’re expecting to occur. This function is used every time we want to test a value.
expect()function is rarely used alone. We usually include it alongside something called a matcher or matcher function that asserts something about the value we’re testing. In the example above,
toBe()is our matcher function. It checks that the value is strictly equal to what we expect. ie: that
1 + 2is strictly equal to
- If the value provided as an argument to
1 + 2) is equal to the value provided as an argument to
3), our test passes. If not, it fails.
If the test passes we see:
In case of test failure we see:
Expanding the unit testing coverage
But even for this simple function sum there are lots of scenarios that we haven’t tested. We would like to catch cases when the function stops working in case if someone modifies it’s implementation down in the future.
So let’s expand the testing coverage by creating additional unit tests:
Here we use
describe(name, fn) block that groups together several related tests in one “test suite”. We added 3 additional test cases besides the initial one, varying the inputs to the function, including a case where sum function throws an error. To test that the function throws this exact error we use
Jest uses “matchers” to test values in different ways. You can find a complete list of matchers here.
Most common of them these:
.toBe(value)– checks the exact equality of two values. In case of objects it checks if it’s the same object according to memory references.
.toEqual(value)– checks that two objects have the same key/value pairs.
.toBeTruthy()– check to ensure that value is false/true in a boolean context.
.toHaveProperty(key, value)– checks for an object.
.toContain– checks if an item is in an array or whether a string is a substring of another string.
.toThrow(error)– checks if a function throws an error when it’s called.
.not– chain this matcher with any other to check the opposite.
This concludes my introduction to Jest unit testing.