When you end up with many tests and test files, new questions start to arise. How do I group the tests? How do I run all the tests in one directory? If there are a lot of them and they're lengthy, is it possible to run them in parallel?
Special test frameworks are used to address these issues. They help you to structure your tests and provide many useful features, such as convenient output formatting. We'll get to know most of these features later in the course. In the JavaScript world, the most popular framework is Jest (by Facebook). Incidentally, it's the framework we use to test all the practice assignments on Hexlet.
Now let's try to create an npm project from scratch and add tests.
Setup and startup
Create a directory called hexlet-jest somewhere on your computer. Go into it and execute this command:
npm init
Answer all the questions from the project initialization script that runs. Make sure that src/index.js appears in the project root.
// This function reverses the string passed to it
export default str => str.split('').reverse().join('');
Jest is a regular npm package that you plug into the developed project locally. Jest is only needed during development, so it's best to install it in the devDependencies section:
# In the project directory
npm i --save-dev jest
For Jest to work correctly with the module system, add the following option to package.json:
"type": "module"
Jest expects the tests to be in the __tests__ directory usually located at the project root. You can create any structure within this directory, and Jest will find all the tests that are there. Test files should be named in this format: <name>.test.js
. Where <name>
, as a rule, corresponds to the name of the module being tested.
Let's write our first test. Create __tests__/index.test.js with the following content:
import reverse from '../src/index.js';
test('reverse', () => {
expect(reverse('hello')).toEqual('olleh');
expect(reverse('')).toEqual('');
});
We'll analyze the structure of this file later, but for now, let's try to do a runtime test:
# Jest supports ECMAScript modules in experimental mode
NODE_OPTIONS=--experimental-vm-modules npx jest
PASS __tests__/index.test.js
✓ reverse (11ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.166s
Ran all test suites.
https://repl.it/@hexlet/js-testing-jest-babel-en#__tests__/index.test.js
Hooray! The tests were successful.
Structure
Let's look at the test file again:
import reverse from '../src/index.js';
test('reverse', () => {
expect(reverse('hello')).toEqual('olleh');
expect(reverse('')).toEqual('');
});
Jest provides two global functions for testing: test
and expect
. You don't need to import them because Jest adds them to the global context.
The test
function helps you describe a specific test and its assertions. There can be any number of test functions. Its first parameter is an arbitrary string, the test description. This string is then displayed on the screen when running tests to simplify debugging.
NODE_OPTIONS=--experimental-vm-modules npx jest
PASS __tests__/index.test.js
✓ reverse (11ms) # test name
The second parameter is the function containing actual tests. Note that this code is not executed immediately. The test
function adds it inside Jest, which decides how and when to run tests. This allows for various optimizations, such as running tests in parallel.
The most unusual thing about this code is the checks themselves; Jest uses "matchers", statements that have a special structure resembling a reference to an object. The general principle of matchers is as follows:
- The
expect()
function is called, to which an actual value is passed - A suitable matcher, such as
toEqual
, will be called on the result returned byexpect()
Code with matchers is similar to ordinary sentences in English. This is done on purpose so that even non-programmers can read them:
// the result of the expression reverse('hello') is expected to be 'olleh'
expect(reverse('hello')).toEqual('olleh');
You'll learn more about matchers in the next lesson.
One of the nicest features of Jest is the way it displays messages about failed tests. Try to make a mistake in the original function and run the tests again:
NODE_OPTIONS=--experimental-vm-modules npx jest
FAIL __tests__/index.test.js
✕ reverse (9ms)
● reverse
expect(received).toEqual(expected) // Object.is equality
Expected: "olleh"
Received: "o|l|l|e|h"
3 | test('reverse', () => {
4 | const str = 'hello';
> 5 | expect(reverse(str)).toEqual('olleh');
| ^
6 | })
7 |
at Object.toEqual (__tests__/index.test.js:5:24)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 1.683s
Ran all test suites.
This output shows not only the expected and actual value, but also the source code of the test file, showing what the actual check was. This is an incredibly useful feature that greatly speeds up analysis of test results and helps with debugging.
Do it yourself
- Follow all of the steps in this lesson
- Upload the code to GitHub
Example of a fully configured package: https://github.com/hexlet-boilerplates/nodejs-package
Recommended materials
Are there any more questions? Ask them in the Discussion section.
The Hexlet support team or other students will answer you.
For full access to the course you need a professional subscription.
A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.