JavaScript Testing with Jest
Jest is a JavaScript testing framework that is designed to be used with little configuration. Jest can be incorporate with Babel, TypeScript, Node, React, Angular, Vue and more, and is one of the most popular and well documented testing frameworks for JavaScript. This blog is designed to be a stripped down and streamlined introduction to Jest, with a brief explanation of the benefits of unit testing and some simple Jest tests you can write to get started. You can view the finished product on GitHub here.
Why Use Unit Testing ?
Unit testing is automated testing that focuses on a single ‘unit’ of your code, such as a function or a module. This makes the tests quick to write and to run, and therefore there can be many tests. Unit tests are specific to such a small piece of your code that it makes tracing errors easy and efficient.
The terms TDD (test-driven development) and BDD (behavior-driven development) refer to approaches to writing tests for a piece of software, and often TDD results in writing unit tests. In TDD, developers write a failing test for what they want their code to achieve first, and then write some code to pass the test. For example, if you want to write a method that gets the average of an array of integers, in TDD you would first write a unit test that fails. You would then adjust the method that is supposed to achieve this until it passes the test. In order to establish more boundaries for this method, more unit tests are written to fail, and then the method adjusted to pass them. This means that writing unit test is part of the process of test driven development.
Additionally, unit testing prevents code from becoming too fragile. Unit tests that verify a module’s functionality should still be applicable when that module has additions or changes. If a new method is added to a class and breaks existing functionality in the class, the existing unit tests will fail. This will indicate to the developer that either the new method has negative side effects, or that the scope of the change is larger than just that method and the existing unit tests need to change. Either way, the developer knows before deployment that their addition or modification breaks existing expected functionality.
Unit testing also provides a sort of make shift documentation for developers. By reading the unit tests for existing modules, a developer can establish what the functionality currently is and should be before making changes or tracking down a bug. This can help establish whether a bug is actually a bug or just missing functionality, and determine the complexity of potential updates to functionality.
Installation
To get started, run two commands in the main folder of your JavaScript folder to install Jest. I will be using npm or node package manager, but you can use yarn instead.
First run :
npm init
to initialize a new npm package and create a package.json file. You can learn more about npm init by reading the documentation, but for the sake of this blog just know that it will create a package.json file where we will store dependencies.
Next run:
npm install --save-dev jest
Note the flag:
--save-dev
This updates the devDependencies in your package, which are used only for local testing and development.
A Very Basic Test
To create our first basic test, we can follow the below steps. These steps mimic the basic documentation from the Jest getting-started page with some slight differences.
- Create a file named product.js and add the below code
2. Create a file named product.test.js with the below code
3. In the package.json file, change the value for “scripts” > “test” from:
"test": "echo \"Error: no test specified\" && exit 1"
to:
"test": "jest"
Your package.json file will look like this:
4. Open your terminal and run:
npm run test
If the code matches, you should expect to see a success message that looks like this:
If you were to purposely make the test fail, by let’s say switching the multiplication sign for an addition sign in the product function in the product.js file, you would see this message:
Jest is specific here, and tells us exactly what the output should have been with Expected: 12, and what the output actually was, Received: 7. This explanation of the test’s failure can help us debug what went wrong and how to fix it.
Testing Classes
In the event we want to test a JavaScript class and not just a file with exported functions, we can export the entire class as a module like so:
and import and use the Example module in a test file like so:
Jest Matchers
To dive deeper into the above test, the function .toBe() is what is called a ‘matcher’ in Jest. This tests value for exact equality, hence our test that matches 30 divided by 5 to equal 6 exactly. The expect() function returns an ‘expectation’ object which we then call the .toBe() matcher on.
Here is a list of some common matchers in Jest and what they test:
- toEqual() => recursively check every field of an object or array
- toBeNull() => matches null only
- toBeTruthy() => match anything treated as true in an if statement
- toBeFalsy() => match anything treated as false in an if statement
- toBeCloseTo() => to test floating point equality
- toBeGreaterThan() => expectation is greater than
- toBeLessThanOrEqual To() => expectation is less than or equal to
- toMatch() => match strings against a regular expressions
- toContain() => check if array or iterable contains particular item
- toThrow() => check if function throws specific error
Asynchronous Tests
One way JavaScript handles asynchronous code is promises, which Jest can also test. The test will return a promise, Jest will wait for it to resolve or reject, and the test will pass or fail accordingly. We can create two asynchronous functions in our Examples class, one that returns a resolved promise with a value, and one that returns a rejected promise with a reason:
Then in our example.test.js file we can write two new tests that test the returns of these asynchronous functions:
In the above example, expect.assertions(1) verifies that a certain number of assertions are called, otherwise the test would not fail.
Conclusion
This has been a basic introduction to writing Jest tests quickly and easily. However, Jest is an extensive framework with a lot of varied functionality for different use cases. You can view the Jest API documentation here or work through the Getting Started module on the Jest website, some of which I have covered here. I highly recommend implementing Jest unit testing into your next JavaScript project, and utilizing their documentation to answer questions and dive deeper into what Jest can do.
Resources: