前端组件单选测试Jest + Enzyme

437 阅读3分钟

技术选型:Jest + Enzyme

Jest

www.jestjs.cn

Jest是Facebook开源的一个前端测试框架,主要用于React和React Native的单元测试,已被集成在create-react-app中。Jest特点:

  1. 易用性:基于Jasmine,提供断言库,支持多种测试风格

  2. 适应性:Jest是模块化、可扩展和可配置的

  3. 沙箱和快照:Jest内置了JSDOM,能够模拟浏览器环境,并且并行执行

  4. 快照测试:Jest能够对React组件树进行序列化,生成对应的字符串快照,通过比较字符串提供高性能的UI检测

  5. Mock系统:Jest实现了一个强大的Mock系统,支持自动和手动mock

  6. 支持异步代码测试:支持Promise和async/await

  7. 自动生成静态分析结果:内置Istanbul,测试代码覆盖率,并生成对应的报告

下面是一个使用Jest进行单元测试的例子:

// sum.js
function sum(a, b) {
  return a + b;
}
module.exports = sum;

// sum.test.js
const sum = require('./sum');

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

Enzyme

enzymejs.github.io/enzyme/

Enzyme是Airbnb开源的React测试工具库库,它功能过对官方的测试工具库ReactTestUtils的二次封装,提供了一套简洁强大的 API,并内置Cheerio,实现了jQuery风格的方式进行DOM 处理,开发体验十分友好。在开源社区有超高人气,同时也获得了React 官方的推荐。

下面是一个使用Enzyme进行单元测试的例子:

import React from 'react';
import { shallow } from 'enzyme';
import MyComponent from './MyComponent';

describe('MyComponent', () => {
  it('renders correctly', () => {
    const wrapper = shallow(<MyComponent />);
    expect(wrapper).toMatchSnapshot();
  });

  it('increments the count when the button is clicked', () => {
    const wrapper = shallow(<MyComponent />);
    const button = wrapper.find('button');
    button.simulate('click');
    expect(wrapper.state('count')).toEqual(1);
  });
});

约定

推荐按照业界通用实践的规范,我们有如下约定:

  • 单元测试的代码放置在 tests 目录下
  • 单元测试文件命名格式使用 xxx.test.ts

相关配置

在项目根目录下创建jest.config.js文件,参数说明可以参考:jestjs.io/docs/en/con…

module.exports = {
  // 块名称的映射关系
  moduleNameMapper: {
    '^#src/(.*)$': '<rootDir>/src/$1',
    '^@tencent/uno-ui': '<rootDir>/../../node_modules/@tencent/uno-ui-v2',
  },
  // 指定哪些文件不需要被收集代码覆盖率
  coveragePathIgnorePatterns: [
    '<rootDir>/.storybook/',
    '<rootDir>/.bin/',
    '<rootDir>/.build/',
    '<rootDir>/.build-docs/',
  ],
  // 指定需要收集代码覆盖率的文件
  collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}'],
  // 指定在运行测试之前需要执行的脚本文件
  setupFilesAfterEnv: ['<rootDir>/src/test/setupTests.ts'],
  // 测试环境
  testEnvironment: 'jsdom',
  // 指定模块解析器,这里指定了一个自定义的解析器用于解决uuid库报错的问题
  resolver: `${__dirname}/src/test/resolver.js`,
};

遇到的问题

因为uuid只提供ESM浏览器导出,而不提供CommonJS导出出现了下面的报错,详情见下面的git链接
github.com/uuidjs/uuid…

  /jest-bug/node_modules/uuid/dist/esm-browser/index.js:1
   ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){export { default as v1 } from './v1.js';
                                                                                     ^^^^^^

   SyntaxError: Unexpected token 'export'

   > 1 | import { v4 } from "uuid";
       | ^
     2 | import { nanoid } from "nanoid";
     3 |
     4 | export function getUuid() {

     at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1773:14)

解决办法

github.com/jestjs/jest…
github.com/microsoft/a…

// https://github.com/jestjs/jest/issues/12770
// https://github.com/microsoft/accessibility-insights-web/pull/5421/commits/9ad4e618019298d82732d49d00aafb846fb6bac7
module.exports = (path, options) =>
  // Call the defaultResolver, so we leverage its cache, error handling, etc.
  options.defaultResolver(path, {
    ...options,
    // Use packageFilter to process parsed `package.json` before the resolution (see https://www.npmjs.com/package/resolve#resolveid-opts-cb)
    packageFilter: (pkg) => {
      // This is a workaround for https://github.com/uuidjs/uuid/pull/616
      //
      // jest-environment-jsdom 28+ tries to use browser exports instead of default exports,
      // but uuid only offers an ESM browser export and not a CommonJS one. Jest does not yet
      // support ESM modules natively, so this causes a Jest error related to trying to parse
      // "export" syntax.
      //
      // This workaround prevents Jest from considering uuid's module-based exports at all;
      // it falls back to uuid's CommonJS+node "main" property.
      //
      // Once we're able to migrate our Jest config to ESM and a browser crypto
      // implementation is available for the browser+ESM version of uuid to use (eg, via
      // https://github.com/jsdom/jsdom/pull/3352 or a similar polyfill), this can go away.
      if (pkg.name === 'uuid') {
        delete pkg.exports;
        delete pkg.module;
      }
      return pkg;
    },
  });

相关框架

ts-jest 是 Jest 的 TypeScript 预处理器。
@types/jest 和 @jest/types 是 Jest 的类型声明文件。
jest-environment-jsdom 是 Jest 的一个测试环境,用于在 Node.js 中模拟浏览器环境。
babel-jest 是 Jest 的一个预处理器,用于处理 ES6+ 语法。
enzyme 是一个流行的 React 组件测试工具。
enzyme-adapter-react-16 是适用于 React 16 版本的 Enzyme 适配器。
@types/enzyme 和 @types/enzyme-adapter-react-16 是 Enzyme 的类型声明文件