Learning the Jest API basics

I learnt about the Jest API usage from the Jest API document. The functions that I used this time are:

  • describe(name, fn)
  • expect(value)
  • .toBe(value)
  • .not
  • .toEqual(value)
  • .toMatchSnapshot(propertyMatchers?, hint?)

Mocking webpack Modules

Jest can be used in projects that use webpack to manage assets, styles, and compilation. I did not use webpack directly, but the Next.js has built-in webpack that used to import CSS modules.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// __tests__/components/homepage/TripList.test.js

import TripList from '../../../components/homepage/TripList';
import TestRenderer from 'react-test-renderer';

describe('Test TripList renders correctly when given no trip', () => {
const noTripRenderer = TestRenderer.create(
<TripList
displayIfNoTrip={testNoTripDisplay}
icon={faCode}
tripInfoList={[]}
title={'Test Trip List'}
/>,
);

I imported css files in the TripList.js file:

1
import styles from '../../css/homepage.module.css';

But Jest does not know what to do with non-JS files at this moment, I got this error (I’ll just include the whole thing to better explain the context):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 Test suite failed to run

Jest encountered an unexpected token

This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

Here's what you can do:
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/en/configuration.html

Details:

/Users/anran/Desktop/TripTime/src/spa/css/trip-card.module.css:1
.card {
^

SyntaxError: Unexpected token '.'

2 |
3 | import React from 'react';
> 4 | import styles from '../../css/trip-card.module.css';
| ^
5 | import PropTypes from 'prop-types';
6 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
7 | import { faStickyNote, faCommentDots } from '@fortawesome/free-solid-svg-icons';

at Runtime._execModule (node_modules/jest-runtime/build/index.js:988:58)
at Object.<anonymous> (components/homepage/TripCard.js:4:1)

Because this was caused by an imported module, the Using with webpack guide gave me a detailed guidance on what to do with non-js imported modules.

I used identity-obj-proxy to mock my CSS modules, and a local fileMock.js to define mocking of other non-JS modules:

1
2
// __mocks__/fileMock.js
module.exports = 'test-file-stub';

I went to the jest.config.js file, and added the moduleNameMapper configuration.

1
2
3
4
5
6
7
8
module.exports = {
moduleNameMapper: {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__tests__/__mocks__/fileMock.js",
"\\.(css|less)$": "identity-obj-proxy"
}
// ... more configs

}

Now I could test my Next.js, and Jest knows what to do with non-JS modules.

TestRenderer

react-test-renderer package provides a React renderer that can be used to render React components to pure JavaScript objects, without depending on the DOM or a native mobile environment.
React website gives clear guidance on the usage of TestRenderer.
The functions I used this time are:

  • TestRenderer.create(), used to create the component renderer
  • testRenderer.toJSON(), used for snapshot testing
  • testRenderer.root, used to get the rendered instance
  • testInstance.findByType(), testInstance.findByProps(), testInstance.findAllByType(), used to find elements in the rendered instance

Snapshot Testing

I first heard the term snapshot testing from my TripTime teammate and it sounded quite intimidating, but it turned out to be a very handy tool for front-end testing.

The expect(value).toMatchSnapshot(propertyMatchers?, hint?) function generates a snapshot locally when it first runs, and in the future runs will test if the new version matches the existing snapshot.
The code looks like this:

1
2
3
4
5
6
7
8
9
describe("Test userHomepage", ()=>{
const userHomePageRenderer = TestRenderer.create(
<UserHomePage name={'Tester'} />,
);
test('Check if UserHomePage matches Snapshot', () => {
expect(userHomePageRenderer.toJSON()).toMatchSnapshot();
})
//...
});

Next Step

What is ES6 proxy?