Jest automatically loads and applies our babel configuration. However, because our project takes advantage of tree shaking with webpack, our babel configuration disables transpiling modules. For Jest to work on our code, we need to make sure that our babel configuration transpiles modules during our tests. We’ll use the NODE_ENV environment variable to generate the proper configuration.
For tree shaking with Webpack, need to disable transpile modules, for jest testing, we need to use commonjs
const isTest = String(process.env.NODE_ENV) === 'test' module.exports = { presets: [['@babel/preset-env', {modules: isTest ? 'commonjs' : false}], '@babel/preset-react'], ... }
Configure Jest’s test environment for testing node or browser code
In our application we’re testing code that should run in the browser, but Jest is intended to test JavaScript that runs in the browser or in node. Let’s create a custom jest config file so we can customize the test environment.
If jest in your app is mean for Node application, then config jest environment to 'node', otherwise, it is mean for broswer, then 'jsdom'
Create 'jest.config.js':
module.exports = { testEnvironment: 'jest-environment-jsdom', //'jest-environment-node', }
If you use 'node' env, then in the test, you cannot access window object.
Support importing CSS files with Jest’s moduleNameMapper
In most real-world applications using webpack, you’re likely using a few loaders. In this lesson we’ll see how to make Jest load our modules properly with a moduleNameMapper so we can test our code that makes use of these loaders.
If you use loader in webpack to css files or graphql files which jest don't understand, you can use this trick to make it work.
Install:
npm i -D react-testing-library
In jest.config.js file:
module.exports = { testEnvironment: 'jest-environment-jsdom', //'jest-environment-node', moduleNameMapper: { '\\.css$': require.resolve('./test/style-mock.js') } }
test/style-mock.js:
module.exports = {}
test file:
import 'react-testing-library/cleanup-after-each'
import React from 'react'
import {render} from 'react-testing-library'
import AutoScalingText from '../auto-scaling-text'
test('renders', () => {
render(<AutoScalingText />)
})
component file:
... import styles from './auto-scaling-text.module.css' class AutoScalingText extends React.Component { ... return ( <div className={styles.autoScalingText} ...
Support using webpack CSS modules with Jest
If you’re using CSS modules with webpack, then we can improve our moduleNameMapper to improve our experience and capabilities testing our components by including the css module property name in our tests using identity-obj-proxy.
If we log out the html inside our test:
import 'react-testing-library/cleanup-after-each' import React from 'react' import {render} from 'react-testing-library' import AutoScalingText from '../auto-scaling-text' test('renders', () => { const {container} = render(<AutoScalingText />) console.log(container.innerHTML) })
We can see:
console.log src/shared/__tests__/auto-scaling-text.js:8 <div style="transform: scale(1,1);"></div>
There is no information about the component class, which should be:
<div className={styles.autoScalingText}
Install:
npm i -D identity-obj-proxy
in jest.config.js:
module.exports = { testEnvironment: 'jest-environment-jsdom', //'jest-environment-node', moduleNameMapper: { '\\.module\\.css$': 'identity-obj-proxy', '\\.css$': require.resolve('./test/style-mock.js') } }
Run:
jest
We can see:
console.log src/shared/__tests__/auto-scaling-text.js:8 <div class="autoScalingText" style="transform: scale(1,1);"></div>
Which include the class name can be a better debug / testing experience.
Generate a Serializable Value with Jest Snapshots
Snapshot testing is a way to simplify writing and maintaining assertions. As noted in the Jest documentation: “The snapshot artifact should be committed alongside code changes, and reviewed as part of your code review process. Jest uses pretty-format to make snapshots human-readable during code review. On subsequent test runs Jest will simply compare the rendered output with the previous snapshot. If they match, the test will pass. If they don't match, either the test runner found a bug in your code that should be fixed, or the implementation has changed and the snapshot needs to be updated.” Let’s see how these work and in what situations we can and should use them.
it is really easy to use snapshot in jest, just remember to update the snaphost when it is necessary by: 'npm t -- --u'
Javascript:
const superHeros = [ {name: 'Dynaguy', powers: ['disintegration ray', 'fly']}, {name: 'Apogee', powers: ['gravity control', 'fly']}, {name: 'Blazestone', powers: ['control of fire', 'pyrotechnic discharges']}, {name: 'Froozone', powers: ['freeze water']}, {name: 'Mr. Incredible', powers: ['physical strength']}, {name: 'Elastigirl', powers: ['physical stretch']}, {name: 'Violet', powers: ['invisibility', 'force fields']}, {name: 'Dash', powers: ['speed']}, {name: 'Jack-Jack', powers: ['shapeshifting', 'fly']} ] export function getFlyingSuperHeros() { return superHeros.filter(hero => { return hero.powers.includes('fly') }) }
import {getFlyingSuperHeros} from '../super-heros';
test('returns super heros that can fly', () => {
const flyingHeros = getFlyingSuperHeros()
expect(flyingHeros).toMatchSnapshot();
})
snapshot:
// Jest Snapshot v1, https://goo.gl/fbAQLP exports[`returns super heros that can fly 1`] = ` Array [ Object { "name": "Dynaguy", "powers": Array [ "disintegration ray", "fly", ], }, Object { "name": "Apogee", "powers": Array [ "gravity control", "fly", ], }, Object { "name": "Jack-Jack", "powers": Array [ "shapeshifting", "fly", ], }, ] `;
React Component:
import React from 'react' import PropTypes from 'prop-types' import AutoScalingText from './auto-scaling-text' import {getFormattedValue} from './utils' class CalculatorDisplay extends React.Component { static propTypes = { value: PropTypes.string.isRequired, } render() { const {value, ...props} = this.props const formattedValue = getFormattedValue( value, typeof window === 'undefined' ? 'en-US' : window.navigator.language, ) return ( <div {...props} css={{ color: 'white', background: '#1c191c', lineHeight: '130px', fontSize: '6em', flex: '1', }} > <AutoScalingText>{formattedValue}</AutoScalingText> </div> ) } } export default CalculatorDisplay