[Testing] Config jest to test Javascript Application -- Part 1

Transpile Modules with Babel in Jest Tests

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:

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
View Code
复制代码
复制代码
import 'react-testing-library/cleanup-after-each'
import React from 'react'
import {render} from 'react-testing-library'
import CalculatorDisplay from '../calculator-display'

test('mounts', () => {
  const {container} = render(<CalculatorDisplay value="0" />)
  expect(container.firstChild).toMatchSnapshot()
})
复制代码

Snapshot:

复制代码
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`mounts 1`] = `
<div
  class="css-12149uf"
>
  <div
    class="autoScalingText"
    style="transform: scale(1,1);"
  >
    0
  </div>
</div>
`;
复制代码

 

Test an Emotion Styled UI with Custom Jest Snapshot Serializers

Part of the power of snapshots is the ability to provide custom serializers. Let’s check out how to use jest-emotion to include our emotion CSS styles in our React component snapshots so we can be made aware of the impact of our CSS changes on our components.

If we look at the implementation of our component, we're getting that className from the css prop, because we're using emotion's babel plugin that will take the css prop and turn it into a className that's generated with a hash. 

 

Snapshot:

复制代码
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`mounts 1`] = `
<div
  class="css-12149uf"
>
  <div
    class="autoScalingText"
    style="transform: scale(1,1);"
  >
    0
  </div>
</div>
`;
复制代码

Using serializer to solve the problem:

Install:

npm install --save-dev jest-emotion
import {createSerializer} from 'jest-emotion';
import * as emotion from 'emotion';

expect.addSnapshotSerializer(createSerializer(emotion));

Snapshot:

复制代码
exports[`mounts 1`] = `
.emotion-0 {
  color: white;
  background: #1c191c;
  line-height: 130px;
  font-size: 6em;
  -webkit-flex: 1;
  -ms-flex: 1;
  flex: 1;
}

<div
  class="emotion-0"
>
复制代码

Now it shows the class and styles, it is much clear to see what has been changed.

 

It is also possible to add global serializer:

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')
    }, 
    snapshotSerializers: ['jest-serializer-path']
}

 

Handle Dynamic Imports using Babel with Jest

If your application is of any significant size, you will want to make use of dynamic imports to for code splitting with webpack. However, dynamic imports are not supported in node. Let’s see how this problem manifests itself and update our babel config to use babel-plugin-dynamic-import-node to simulate this in our test environment.

dynamic import:

import loadable from 'react-loadable'

const CalculatorDisplay = loadable({
  loader: () => import('calculator-display').then(mod => mod.default),
  loading: () => <div style={{height: 120}}>Loading display...</div>,
})

Install:

npm i -D babel-plugin-dynamic-import-node

Config babel:

复制代码
plugins: [
    '@babel/plugin-syntax-dynamic-import',
    '@babel/plugin-proposal-class-properties',
    '@babel/plugin-proposal-object-rest-spread',
    [
      'babel-plugin-emotion',
      {
        hoist: isProd,
        sourceMap: !isProd,
        autoLabel: !isProd,
        labelFormat: '[filename]--[local]',
      },
    ],
    'react-loadable/babel',
    isTest ? 'babel-plugin-dynamic-import-node': null
  ].filter(Boolean),
复制代码

 

posted @   Zhentiw  阅读(917)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2016-11-14 [AngularJS NG-redux] Handle Asynchronous Operations with Middleware
2016-11-14 [AngualrJS NG-redux] Map State and Dispatchers to Redux
2016-11-14 [Scss Flex] Reuse Flexbox Styles With A Sass Mixin
2015-11-14 [AngularJS] Use ng-model-options to limit $digest
2015-11-14 [Flux] Component / Views
2015-11-14 [Flux] Stores
2014-11-14 [RSpec] LEVEL 2 CONFIGURATION & MATCHERS
点击右上角即可分享
微信分享提示