About the cover illustration
6. Write an end-to-end test
2.2. INTRODUCTION TO JEST
2.2.1. Writing a sanity test
Have you ever spent hours debugging a website only to realize you were viewing a cached version? I have, and it was incredibly frustrating. Now, whenever I test code in an old browser, I add a console log in my code to make sure I’m running the latest version. The console log is a little sanity test that stops me from spending hours debugging something that was never broken.
My console log trick has saved me from testing cached content on Internet Explorer many times. You should add a similar sanity check when you write unit tests. A failing test will lead you on a debugging spree, and it would be very annoying to find out that it was actually the test setup that was broken rather than the source code.
A sanity test is a test that always passes. If the sanity test fails, you know there is a problem with the test setup.
First, you need to install Jest. Run the following command from the command line to install Jest and save it as a development dependency in the package.json file:
npm install --save-dev jest
If you open the package.json file, you’ll see Jest has automatically been
added to the devDependencies object by npm.
You’ll use an npm script to run unit tests in Jest. It’s useful to separate different types of tests into their own scripts, so that you can run only one type of test if you need to.
Open the package.json file in a text editor, and add the following line to the scripts object:
'test:unit': 'jest'
Note
If you’re using Windows, you should add a no-cache flag to the jest command to avoid potential errors. The full script will look like "test:
unit": "jest --no-cache".
Now you can add the unit test script to the test script. Tests are about quick feedback. Like a startup company, if a test is going to fail, it should fail fast. It would be annoying to wait 30 seconds for your unit tests to run, only to find your tests fail in the lint script because of a trailing comma. To avoid this, you should arrange the scripts in your test script from the
quickest to the slowest.
Update the test script as follows to also run the unit tests after linting:
'test': 'npm run lint && npm run test:unit',
Now run the unit test script by entering the following command in the command line:
npm run test:unit
You’ll see a Jest error. It couldn’t find any matching files! If you look at the error output, you’ll see the following line:
testMatch: **/__tests__/**/*.js?(x),**/?(*.)(spec|test).js?
(x)
This is the default glob pattern Jest uses to find test files in the project. For non-glob speakers, this pattern means that Jest matches .js and .jsx files inside __tests__ directories, as well as all files with a .spec.js or .test.js extension.
Definition
Globs are the patterns used to match files. Jest uses the Node glob module to match files. You can read more about globs in the glob primer section of the glob npm page at www.npmjs.com/package/glob#glob-primer.
You’re going to write your tests using a .spec.js file extension, so the test
file for Item.vue file will be named Item.spec.js.
Note
Here, spec stands for specification, because unit tests are specifications for how your code should behave. Each test specifies a result to expect when the function you are testing is called.
Create a directory called __tests__ in the same directory as the component you’re testing, and create a file named Item.spec.js inside the __tests__
directory. The file path from the root of your project will be src/components/__tests__/Item.spec.js.
Tip
It’s good practice to keep unit tests as close to the code they’re testing as possible. It makes it easier for other developers to find the test files for components or modules.
To define a unit test in Jest you use the test function. The test
function takes two parameters: the first parameter is a string that identifies the test in the test report. You’ll see what I mean by the test report when you run Jest in a second.
The second parameter to test is a function that contains the test code.
Jest parses each test function in a test file, runs the test code, and reports back whether the test passed or failed.
A test fails if the test function throws an error. You can imagine Jest running the tests in a big try/catch statement. If the catch statement runs, Jest reports the test as failed.
In src/components/__tests__/Item.spec.js, add the next code to define a sanity test.
Listing 2.1. Sanity test
test('sanity test', () => { return
})
Run the unit test command npm run test:unit. The sanity test passes, so you know the test system is set up correctly.
Now you can run the test script—npm run test:unit—confident that it runs tests as expected. If the test script throws an error, you’ll know the problem is with the test files rather than the test setup.
Let’s take a step back and think about what Jest happened. Sorry. First, you ran the test:unit script, which ran Jest. Jest matched Item.spec.js using the default glob pattern, found the test function, and ran the
second argument (the test), which returned without throwing an error. Jest saved the result and reported back that the test passed.
Note
As well as running Jest explicitly, you can also run it in watch mode.
Watch mode watches for file changes and reruns tests for files that are updated. You use watch mode by calling Jest with a --watch flag. For example, to run the unit test script in watch mode, you would run npm run test:unit -- --watch. The first double dash adds the
watch argument to the npm script. Note that watch will rerun all tests if your test script has a --no-cache flag.
Great. Now lint the project to make sure the code is formatted correctly.
Run the following command to kick off the lint script:
npm run lint
Oh no, errors! The lint errors say that test is undefined. The problem is the linter does not know you’re running test files in Jest, so it doesn’t know that the test function will be defined globally.
To stop these linting errors, you need to update your ESLint configuration.
Open package.json and go to the eslintConfig object, and then find the env object. This object tells ESLint what environment you are
running code in. Add a jest property set to true. Your env object should look like this:
'env': {
'node': true, 'jest': true },
Note
ESLint can support multiple environments, like jest and browser. If you’re wondering why the eslintConfig in your project doesn’t have env set to browser, it’s because it’s added under the hood by the eslint-vue plugin.
Run the lint script again: npm run lint. Now the linter knows that the test is available globally, so it won’t throw any errors. You might have some unrelated errors about formatting issues. Resolve any linting errors before moving on.
Note
If you have trouble fixing an ESLint error, Google the error’s name. You’ll find an ESLint page explaining the rule in detail and how to fix it.
Now you have a unit test script with a passing sanity test set up. You can improve the sanity test by using a test assertion.