Setup jest from scratch in a vanilla js project.

Manuel Artero Anguita ๐ŸŸจ - May 13 '22 - - Dev Community

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
Enter fullscreen mode Exit fullscreen mode
  ...
  "html-webpack-plugin": "^5.5.0",
+ "jest": "^28.0.3",
  "postcss-loader": "^6.2.1",
  ...
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

โžก๏ธ 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/
Enter fullscreen mode Exit fullscreen mode

This implies a subtle tuning at jest.config.js

...
- roots: ['src']
+ roots: ['../src'],
...
Enter fullscreen mode Exit fullscreen mode

Plus considering the new path at package.json

...
- "test": "jest"
+ "test": "jest --config=config/jest.config.js"
...
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  ...
+ "@babel/core": "^7.17.9",
+ "@babel/preset-env": "^7.16.11",
+ "babel-jest": "^28.0.3",
  ...
Enter fullscreen mode Exit fullscreen mode

Plus these lines at jest.config.js

+ transform: {
+   '\\.[jt]sx?$': 'babel-jest',
+ },
Enter fullscreen mode Exit fullscreen mode

And (if you didn't have one already) a brand new config/babel.config.json

{
    "env": {
        "test": {
            "presets": [
                "@babel/preset-env"
            ],
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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,
        },
    });
});
Enter fullscreen mode Exit fullscreen mode

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:

  1. At config/jest.config.js
+setupFiles: ['./jest-env-vars.js']
Enter fullscreen mode Exit fullscreen mode
  1. config/jest-env-vars.js
process.env.API_URL = 'https://fake-url.api.test';
process.env.API_KEY = '==API KEY TEST==';
Enter fullscreen mode Exit fullscreen mode

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"
+            ]
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  1. At config/jest.config.js
+ setupFilesAfterEnv: ['./setup-tests.js'],
Enter fullscreen mode Exit fullscreen mode
  1. config/setup-tests.js
import '@testing-library/jest-dom';
Enter fullscreen mode Exit fullscreen mode

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 ๐Ÿ’š.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player