使用Jest进行React单元测试

219 阅读2分钟

1.单元测试的定义

测试根据不同的维度有多种分类方式,按照测试阶段主要有单元测试、集成测试和系统测试。单元测试(Unit Tesing)是针对程序的最小单位,对代码进行正确性检验的一种测试手段。在过程化编程中,最小单元是单个程序、函数、过程等;对于面向对象编程,最小单元是对象方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。

2.单元测试的意义

  • 提升效率:通过 mock 数据,及早发现问题,而越早发现Bug,造成的浪费就会越小
  • 结构设计:单元测试可以充当一个设计工具,有助于开发人员去思考代码结构的设计,让代码更加有利于测试。特别是先写测试,迫使我们把程序设计成易于调用和可测试的,即迫使我们解除软件中的耦合

3.安装与配置

下文介绍使用 JestReact 程序进行单元测试,采用React Testing Library 作为测试工具库。

安装Jest
yarn add --dev jest

yarn add --dev jest-environment-jsdom jest-less-loader 
yarn add @testing-library/jest-dom @testing-library/react
配置Jest
jest --init

✔ 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? … yes
✔ Which provider should be used to instrument code for coverage? › babel
✔ Automatically clear mock calls and instances between every test? … no
配置 Babel
yarn add --dev babel-jest @babel/core @babel/preset-env @babel/preset-react

在项目的根目录下创建 babel.config.js ,通过配置 Babel 使其能够兼容当前的 Node 版本。

module.exports = {
  presets: [
    ['@babel/preset-env', {targets: {node: 'current'}}],
    '@babel/preset-react'
  ],
};

在package.json中配置如下内容

"scripts": {
    "test": "jest",
 },
"resolutions": {
    "jest-environment-jsdom": "27.4.6"
 },

在jest.config.js中配置如下内容

module.exports = {
  setupFilesAfterEnv: ['@testing-library/jest-dom'],
  testEnvironment: 'jsdom',
  clearMocks: true,
  coverageDirectory: "coverage",
  moduleDirectories: [
    "node_modules", "src"
  ],
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1',
    '\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
      '<rootDir>/test/fileMock.js',
    '\\.(css|less)$': '<rootDir>/test/styleMock.js',
  },
  transform: {
    "^.+\\.(js|jsx)$": "babel-jest",
    '\\.(less|css)$': 'jest-less-loader'
  },
  transformIgnorePatterns: [
    '/node_modules/(?!antd|@ant-design|rc-.+?|@babel/runtime).+(js|jsx)$',
  ],
}

其中,fileMock.jsstyleMock.js文件内容均为

module.exports = {}

matchMedia.mock.js文件内容如下

Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: jest.fn().mockImplementation(query => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: jest.fn(), // deprecated
    removeListener: jest.fn(), // deprecated
    addEventListener: jest.fn(),
    removeEventListener: jest.fn(),
    dispatchEvent: jest.fn(),
  })),
});

4.基本使用

5.常见问题

(1)Jest encountered an unexpected token
10195009358.png
解决方案:
 moduleNameMapper: {
    "^@/(.*)$": "<rootDir>/src/$1",
    '\\.(css|less)$': '<rootDir>/tests/styleMock.js',
    'antd/es/locale/zh_CN':'<rootDir>/tests/styleMock.js'
  },
(2)无法读取 props.history.push
15240329886 (1).png
解决方案:
 test('test', async () => {
    const mProps = { history: { push: jest.fn() } }   
    await render(
        <AppStore {...mProps} />
    )
    expect(mProps.history.push).toBeCalledWith('/app/shelve')
  })
(3)无法读取 useContext 传入的值
11894001133 (1).png
解决方案: