Overview
Jest is an open JavaScript testing library from Facebook. Its slogan is "Delightful JavaScript Testing". While Jest can be used to test any JavaScript library, it shines when it comes to React and React Native.
This is no surprise as both React and Jest come from Facebook, which is a major user of both. In this tutorial I'll show you eight different aspects of Jest that make it such a delight for testing React applications.
1. Jest Is a Breeze to Set Up
Jest is pretty simple to install on its own. You can just install it in an empty directly using either npm or yarn. I prefer yarn. See 6 Things That Make Yarn the Best JavaScript Package Manager to understand why. It's as simple as:
yarn add --dev jest
If you prefer npm, then type:
npm install --save-dev jest
Before we can test, let's write some code to test. Here is palindrome.js:
function isPalindrome(s) { const count = s.length - 1 if count < 2 { return true } for (i = 0; i < (count + 1) / 2; ++i) { if (s[i] !== s[count - i]) return false } return true } module.exports = isPalindrome
Here is a jest test in a file called palindrome.test.js:
const isPalindrome = require('./palindrome') test('it detects palindromes', () => { expect(isPalindrome('palindrome')).toBe(false) expect(isPalindrome('')).toBe(true) expect(isPalindrome('a')).toBe(true) expect(isPalindrome('gg')).toBe(true) expect(isPalindrome('pop')).toBe(true) expect(isPalindrome('1212')).toBe(false) })
To run the tests, add this to package.json:
"scripts": { "test": "jest" }
You can now run the tests with yarn test
or npm test
:
> yarn test yarn run v1.1.0 warning package.json: No license field $ jest PASS ./palindrome.test.js ✓ it detects palindromes (6ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 1.667s Ran all test suites. ✨ Done in 3.15s.
This is pretty simple. But if you use react-create-app to create your React project, you don't even have to do that. The jest package comes bundled in, and you can just start writing tests immediately.
2. Jest Is Lightning Fast
Jest is fast. Very fast. When your tests are CPU bound, it can shave significant time from your test runs. Airbnb switched from Mocha to Jest, and their total test runtime dropped from more than 12 minutes to only 4.5 minutes on a heavy-duty CI machine with 32 cores. Local tests used to take 45 minutes, which dropped to 14.5 minutes.
What makes Jest so fast? It's a combination of several factors:
- Parallelization: this is pretty obvious, and other test frameworks use it too.
- Run slowest tests first: this ensures all cores are utilized to the max.
- Caching babel transforms: reduces CPU-intensive babel transforms.
3. Jest Is a One-Stop Shop
Jest comes with built-in matchers, spies, and its own extensive mocking library. It used to be based on Jasmine, so it inherited all of Jasmine's goodness. But in more recent versions Jest departed from Jasmine, yet kept the same functionality and added its own flavor and improvements.
When comparing it to a bespoke testing solution based on Mocha, it's clear that ease of use is a major concern of Jest design.
4. Jest Has Awesome Mocks
Mocking is an incredibly important part of unit testing. This is especially important if you care about fast tests (and who doesn't?).
Mocking allows you to replace irrelevant dependencies that may be slow and even control time for code that relies on timing. Jest lets you fully control your dependencies and master time.
Simple Mock Functions
Mocking dependencies is a long-time tradition of unit tests. If your code is reading a file, writing to a file, calls some remote service or is accessing a database, it may be slow and it may be complicated to configure and clean up after the test. When running in parallel, it may not even be possible to control properly.
In these cases, it is better to replace the real dependency with a mock function that does nothing but just records the fact it was called, so you can verify the workflow. The jest.fn()
mock function lets you provide canned return values (for multiple consecutive calls), and it records how many times it was called and what the parameters were in each call.
Manual Module Mocks
Sometimes you may need to replace a whole module with its data rather than a couple of functions. Jest lets you do that by placing your own module with the same name in a __mocks__
sub-directory.
Whenever your code is using the target module, it will access your mock rather than the real module. You can even selectively choose for some tests to use the original module by calling jest.Unmock('moduleName')
.
Timer Mocks
Timing is the bane of unit tests. What if you want to test code that times out after a minute? Code that fires every 30 seconds? Special code that runs a reconciliation algorithm at the end of the month?
Those are difficult to test. You can either succumb to the timing requirements of the original code (and then your tests will be very slow), or you can manipulate time, which is much more useful. Jest lets you control the following timer-related functions:
- setTimeout()
- setInterval()
- clearTimeout()
- clearInterval()
ES6 Class Mocks
Jest fully supports ES6 classes and provides various ways to mock them:
- Automatic mock: lets you spy on calls to constructor and all methods, but always returns undefined.
- Manual mock: implement your own mock in the
__mocks__
sub-directory. - Mock the class factory with a higher-order function.
- Selective mocking using
mockImplementation()
ormockImplementationOnce()
.
5. Jest Supports TypeScript
TypeScript is a popular typed superset of JavaScript that compiles to plain JavaScript. Jest supports TypeScript via the ts-jest package. It describes itself as a TypeScript preprocessor with source map support for Jest and has a very active community.
6. Jest Has Got You Covered
Jest has built-in coverage reports. Your tests are only as good as their coverage. If you test only 80% of your code, then bugs in the other 20% will be discovered only in production.
Sometimes, it makes sense from a business perspective to skip testing for some parts of the system. For example, internal tools that your own expert engineers use and change frequently may not need the same level of rigorous testing as your production code. But, at any rate, this should be a conscious decision, and you should be able to see exactly the test coverage of different parts of your system.
Here is how to generate a coverage report for the simple palindrome example:
> yarn test --coverage yarn run v1.1.0 warning package.json: No license field $ jest "--coverage" PASS ./palindrome.test.js ✓ it detects palindromes (4ms) -------------- |----------|----------|----------|----------| File | % Stmts | % Branch | % Funcs | % Lines | -------------- |----------|----------|----------|----------| All files | 100 | 100 | 100 | 100 | palindrome.js | 100 | 100 | 100 | 100 | -------------- |----------|----------|----------|----------| Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 1.712s Ran all test suites. ✨ Done in 3.41s.
7. Jest Does Snapshots
Snapshot testing is great. It lets you capture a string that represents your rendered component and store it in a file. Then you can compare it later to ensure that the UI didn't change. While it is ideal for React and React Native apps, you can use snapshots for comparing serialized values of other frameworks. If you actually change your UI then you need to update your snapshot files to reflect it of course.
8. Jest Does Delta Testing With Watch
Jest can run in watch mode, where it runs the tests automatically whenever you change the code. You run it with the --watchAll
command-line argument, and it will monitor your application for changes. I ran jest in watch mode and introduced a bug on purpose to palindrome.js, and here is the result:
FAIL ./palindrome.test.js ✕ it detects palindromes (11ms) ● it detects palindromes expect(received).toBe(expected) // Object.is equality Expected value to be: true Received: false 6 | expect(isPalindrome('a')).toBe(true) 7 | expect(isPalindrome('gg')).toBe(true) > 8 | expect(isPalindrome('pop')).toBe(true) 9 | expect(isPalindrome('1212')).toBe(false) 10 | }) 11 | at Object.<anonymous>.test (palindrome.test.js:8:30) Test Suites: 1 failed, 1 total Tests: 1 failed, 1 total Snapshots: 0 total Time: 0.598s, estimated 1s Ran all test suites. Watch Usage: Press w to show more.
Conclusion
Jest is a fast testing framework that's easy to set up. It is actively developed and used by Facebook to test all their React applications as well as by many other developers and companies.
It has all you need in one convenient package, supports TypeScript, and IMO is the best option for React and React Native application testing. It is also very to migrate from other testing solutions.
Remember, React has grown in popularity. In fact, we have a number of items in the Envato Market that are available for purchase, review, implementation, and so on. If you’re looking for additional resources around React, don’t hesitate to check them out.
No comments:
Post a Comment