From time to time I see myself setting up a web project from scratch. Something super-easy if you use create-react-app
(if you're using React)... but not that smooth in vanilla.
β‘οΈ This occasion we needed at work a vanilla js project.
A co-worker was setting up webpack and asked me to enable jest.
And I was like, "sure this should take 10 mins at most".
It took longer.
1. Install jest
First step is obv.
$ yarn add -D jest
...
"html-webpack-plugin": "^5.5.0",
+ "jest": "^28.0.3",
"postcss-loader": "^6.2.1",
...
2. Base Config
Next, call the jest CLI
tool which will create the base config:
$Β» jest --init
The following questions will help Jest to create a suitable configuration for your project
β Would you like to use Jest when running "test" script in "package.json"? β¦ yes
β Would you like to use Typescript for the configuration file? β¦ no
β Choose the test environment that will be used for testing βΊ jsdom (browser-like)
β Do you want Jest to add coverage reports? β¦ no
β Which provider should be used to instrument code for coverage? βΊ babel
β Automatically clear mock calls, instances and results before every test? β¦ no
β‘οΈ Note: Here we've taken one relevant decision. test environment is
jsdom
since we're building a web app, we want a browser-like environment.
[3]. Optional Step
We try to reduce dot-files-pollution at root folder. Basically, we gather every tool-related config file and move them to config/
.
$Β» mv jest.config.js config/
This implies a subtle tuning at jest.config.js
...
- roots: ['src']
+ roots: ['../src'],
...
Plus considering the new path at package.json
...
- "test": "jest"
+ "test": "jest --config=config/jest.config.js"
...
4. Configure Jest
Jest runs over node. From the docs:
Jest runs the code of your project as JavaScript, hence a transformer is needed if you use some syntax not supported by Node out of the box.
Since we're playing with browser-things, we need babel-jest
.
$Β» yarn add -D babel-jest @babel/preset-env @babel/core
...
+ "@babel/core": "^7.17.9",
+ "@babel/preset-env": "^7.16.11",
+ "babel-jest": "^28.0.3",
...
Plus these lines at jest.config.js
+ transform: {
+ '\\.[jt]sx?$': 'babel-jest',
+ },
And (if you didn't have one already) a brand new config/babel.config.json
{
"env": {
"test": {
"presets": [
"@babel/preset-env"
],
}
}
}
5. Try the thing with a real test
One of the things I hate the most from tutorials is that they use fake-hello-world examples.
Our real world test is:
import axios from 'axios';
import api from './api';
jest.mock('axios');
afterEach(() => {
jest.clearAllMocks();
});
test('getLayoutData() [200]', async () => {
axios.get.mockResolvedValue({ status: 200, data: { layout: 'hello world' } });
const id = 42
const layoutData = await api.getLayoutData(id, { someQueryParam: true });
expect(layoutData).toEqual({ layout: 'hello world' });
expect(axios.get).toHaveBeenCalledTimes(1);
expect(axios.get).toHaveBeenCalledWith('https://fake-url.api.test/v1/layouts/42', {
headers: {
'x-api-key': '==API KEY TEST==',
},
params: {
someQueryParam: true,
},
});
});
I bet you have one of those at your real world problems
6. ENV vars
Our code relies on two env vars; these two values: 'https://fake-url.api.test'
and '==API KEY TEST=='
.
Let's add those:
- At
config/jest.config.js
+setupFiles: ['./jest-env-vars.js']
config/jest-env-vars.js
process.env.API_URL = 'https://fake-url.api.test';
process.env.API_KEY = '==API KEY TEST==';
AND... It fails π€
7. Enable async
await
Straight to the point what it's happening is that we need yet another babel plugin: @babel/plugin-transform-runtime
Enable the plugin at babel.config.js
{
"env": {
"test": {
"presets": [
"@babel/preset-env"
],
+ "plugins": [
+ "@babel/plugin-transform-runtime"
+ ]
}
}
}
8. Final touches π
At some point we'll rendering some component, and testing those you want the handy methods toBeInTheDocument()
, toBeVisible()
from Testing Library
$Β» yarn add -D @testing-library/jest-dom
- At
config/jest.config.js
+ setupFilesAfterEnv: ['./setup-tests.js'],
config/setup-tests.js
import '@testing-library/jest-dom';
Summary
With just 7 dev-dependencies,
@babel/core
@babel/preset-env
@babel/plugin-transform-runtime
@testing-library/jest-dom
babel-jest
jest
jest-environment-jsdom
And just 4 config files,
- babel.config.json
- jest-env-vars.js
- jest.config.js
- setup-tests.js
we got $Β» yarn test
working π
--
Cover image People illustrations by Storyset
Thanks for reading π.
Top comments (5)
Thank you for this fantastic post Manuel! Was super easy to follow along
Glad you found this useful! is it there any extra area you'd like to read about ?
Actually yes! From your βimportβ syntax, I assume you are using ECMAScript Modules? Could you maybe elaborate on specifically what all you do that allowed you to use jest with .mjs files?
Sure!
who is in charge of reading cool-js and getting boring-js on return.
In particular, you may find the babeljs.io/docs/en/babel-preset-en... useful
Ah ok, I see now. And yes, going to babelβs documentation was much more helpful than all the time Iβve spent on Jest documentation.
So babel is just handling everything, then. Thrilled to get the quick and helpful response, but the answer is a total bummer. I kinda figured Jest would be almost fully converted by now