乐趣区

关于前端:react-项目单元测试技术方案

Why

The more your tests resemble the way your software is used, the more confidence they can give you.

背景

该我的项目是用 umi 框架搭建,其本身内置了 jest 测试框架

  • umi 内置的 jest 配置
{
    collectCoverageFrom: ['index.{js,jsx,ts,tsx}',
      hasSrc && 'src/**/*.{js,jsx,ts,tsx}',
      isLerna && !args.package && 'packages/*/src/**/*.{js,jsx,ts,tsx}',
      isLerna &&
        args.package &&
        `packages/${args.package}/src/**/*.{js,jsx,ts,tsx}`,
      '!**/typings/**',
      '!**/types/**',
      '!**/fixtures/**',
      '!**/examples/**',
      '!**/*.d.ts',
    ].filter(Boolean),
    moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json'],
    moduleNameMapper: {'\\.(css|less|sass|scss|stylus)$': require.resolve('identity-obj-proxy'),
    },
    setupFiles: [require.resolve('../../helpers/setupFiles/shim')],
    setupFilesAfterEnv: [require.resolve('../../helpers/setupFiles/jasmine')],
    testEnvironment: require.resolve('jest-environment-jsdom-fourteen'),
    testMatch: [`${testMatchPrefix}**/?*.(${testMatchTypes.join('|')}).(j|t)s?(x)`,
    ],
    testPathIgnorePatterns: ['/node_modules/', '/fixtures/'],
    transform: {'^.+\\.(js|jsx|ts|tsx)$': require.resolve('../../helpers/transformers/javascript',),
      '^.+\\.(css|less|sass|scss|stylus)$': require.resolve('../../helpers/transformers/css',),
      '^(?!.*\\.(js|jsx|ts|tsx|css|less|sass|scss|stylus|json)$)': require.resolve('../../helpers/transformers/file',),
    },
    verbose: true,
    transformIgnorePatterns: [// 加 [^/]*? 是为了兼容 tnpm 的目录构造
      // 比方:_umi-test@1.5.5@umi-test
      // `node_modules/(?!([^/]*?umi|[^/]*?umi-test)/)`,
    ],
    // 用于设置 jest worker 启动的个数
    ...(process.env.MAX_WORKERS
      ? {maxWorkers: Number(process.env.MAX_WORKERS) }
      : {}),
  }
  • 合并配置局部代码如下:
const config = mergeConfig(createDefaultConfig(cwd, args),
  packageJestConfig,
  userJestConfig
);

其源码在此:点击跳转

技术栈抉择

因为 umi 内置的 jest 配置是不能满足咱们我的项目的需要的,jest 是 JavaScript 的测试框架,如果须要对 dom 做单元测试,须要引入更多测试库

Jest

jest 是 umi 自带的,这个是根底,须要对其有根本的意识,其文档如下:
https://jestjs.io/zh-Hans/

ts-jest

ts-jest 是让咱们能用 Typescript 写测试代码的转换工具,也是 umi 内置配置,很少须要更改,如需更改可参考官网文档:
https://kulshekhar.github.io/ts-jest/docs/

React Testing Library

React Testing Library 是 测试 React 组件的解决方案,其蕴含了 React 官网的测试工具 react-dom/test-utils 的 API,也是 React 官网举荐的测试 DOM 库。
至于为什么不必老牌测试库 Enzyme , 能够看作者的 blog 这篇文章:
https://kentcdodds.com/blog/introducing-the-react-testing-library

平时写测试代码时更多接触的是 React Testing Library , 其文档如下:
https://testing-library.com/docs/react-testing-library/intro
除了 @testing-library/react 外,平时 @testing-library 旗下的其余库,例如 @testing-library/user-event 用来模仿交互的。@testing-library/react 是基于 @testing-library/dom 的,所以对 @testing-library/dom 有根本的理解,有助于深刻了解 @testing-library/react

Mock Service Worker

Mock Service Worker (MSW) 是用来 mock Axios 的申请后果的,执行 REST/GraphQL API
其文档如下:https://github.com/mswjs/msw

应用疏导

单元测试编写

这里不做过多介绍,能够参考官网 example 及其他领导文章,以及现有的单元测试代码,分享一下如下几篇

  1. 官网 demo:https://testing-library.com/docs/react-testing-library/example-intro
  2. 作者 blog—Common mistakes with React Testing Library — https://kentcdodds.com/blog/common-mistakes-with-react-testing-library
  3. 测试 react-toolkit — https://ogzhanolguncu.com/blog/testing-react-redux-toolkit-with-typescript

注:写单元测试可能加深你对代码的了解以及对本人代码的信任度

运行

单元测试环境曾经搭建好了,只须要执行对应的命名就能够了

"test": "umi test --no-cache", // 一次性执行单元测试
"test:watch": "umi test --watch", // 实时监听代码变动,执行
"test:clear": "umi test --clearCache",
"test:coverage": "umi test --coverage", // 执行,输入笼罩报告 

配置

有些状况现有的配置不能满足本人的需要,能够依据状况更改配置

  1. jest.config.js
  2. ./src/setupTests.ts

覆盖率

应用了 Istanbul 代码笼罩工具,对于笼罩指标参考:
阮一峰这篇文章 — http://www.ruanyifeng.com/blog/2015/06/istanbul.html
目前我的项目确定如下的笼罩指标:

global: {
  branches: 90,
  functions: 90,
  lines: 90,
  statements: 90,
},

应用 yarn test:coverage 能够测试输入覆盖率报告

踩坑过程问题记录

记录一些问题:以防反复踩坑

  1. Jest test fails : TypeError: window.matchMedia is not a function
  2. Jest + react-testing-library: Warning update was not wrapped in act()
  3. Uncaught [Error: Invalid hook call. Hooks can only be called inside of the body of a function component — mock module 在 setupTests.ts 中引入
  4. The module factory of jest.mock() is not allowed to reference any out-of-scope variable — 用 require 在外面解决, 参考:https://github.com/facebook/jest/issues/2567

后续打算

  1. 抽离公共测试代码,使写单测更加丝滑
  2. 补充单元测试,满足覆盖率指标
  3. 端对端测试搞起来
  4. 接入到 CI/CD 中
退出移动版