与软件操作行为越接近的测试,越能给予你信心。

概览

本节主要讲述如何在组件库中引入jest以及@testing-library/react,而不会深入单元测试的学习。

如果你对下列问题感兴趣:

  1. What-单元测试是什么?
  2. Why-为什么要写单元测试?
  3. How-编写单元测试的最佳实践?

那么可以看看以下文章:

本节所有代码可在仓库chapter-4分支中获取。

相关配置

安装依赖:

yarn add jest ts-jest @testing-library/react @testing-library/jest-dom identity-obj-proxy @types/jest @types/testing-library__react --dev
  • jest: JavaScript 测试框架,专注于简洁明快;
  • ts-jest:为TypeScript编写jest测试用例提供支持;
  • @testing-library/react:简单而完整的React DOM测试工具,鼓励良好的测试实践;
  • @testing-library/jest-dom:自定义的jest匹配器(matchers),用于测试DOM的状态(即为jestexcept方法返回值增加更多专注于DOMmatchers);
  • identity-obj-proxy:一个工具库,此处用来mock样式文件。

新建jest.config.js,并写入相关配置,更多配置可参考jest 官方文档-配置,只看几个常用的就可以。

jest.config.js

module.exports = {
  verbose: true,
  roots: ['<rootDir>/components'],
  moduleNameMapper: {
    '\\.(css|less|scss)$': 'identity-obj-proxy',
    '^components$': '<rootDir>/components/index.tsx',
    '^components(.*)$': '<rootDir>/components/$1',
  },
  testRegex: '(/test/.*|\\.(test|spec))\\.(ts|tsx|js)$',
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
  testPathIgnorePatterns: ['/node_modules/', '/lib/', '/esm/', '/dist/'],
  preset: 'ts-jest',
  testEnvironment: 'jsdom',
};

修改package.json,增加测试相关命令,并且代码提交前,跑测试用例,如下:

package.json

"scripts": {
  ...
+  "test": "jest",                         # 执行jest
+  "test:watch": "jest --watch",           # watch模式下执行
+  "test:coverage": "jest --coverage",     # 生成测试覆盖率报告
+  "test:update": "jest --updateSnapshot"  # 更新快照
},
...
"lint-staged": {
  "components/**/*.ts?(x)": [
    "prettier --write",
    "eslint --fix",
+   "jest --bail --findRelatedTests",
    "git add"
  ],
  ...
}

修改gulpfile.js以及tsconfig.json,避免打包时,把测试文件一并处理了。

gulpfile.js

const paths = {
  ...
- scripts: ['components/**/*.{ts,tsx}', '!components/**/demo/*.{ts,tsx}'],
+ scripts: [
+   'components/**/*.{ts,tsx}',
+   '!components/**/demo/*.{ts,tsx}',
+   '!components/**/__tests__/*.{ts,tsx}',
+ ],
};

tsconfig.json

{
- "exclude": ["components/**/demo"]
+ "exclude": ["components/**/demo", "components/**/__tests__"]
}

编写测试用例

<Alert />比较简单,此处只作示例用,简单进行一下快照测试。

在对应组件的文件夹下新建__tests__文件夹,用于存放测试文件,其内新建index.test.tsx文件,写入以下测试用例:

components/alert/tests/index.test.tsx

import React from 'react';
import { render } from '@testing-library/react';
import Alert from '../alert';

describe('<Alert />', () => {
  test('should render default', () => {
    const { container } = render(<Alert>default</Alert>);
    expect(container).toMatchSnapshot();
  });

  test('should render alert with type', () => {
    const kinds: any[] = ['info', 'warning', 'positive', 'negative'];

    const { getByText } = render(
      <>
        {kinds.map(k => (
          <Alert kind={k} key={k}>
            {k}
          </Alert>
        ))}
      </>,
    );

    kinds.forEach(k => {
      expect(getByText(k)).toMatchSnapshot();
    });
  });
});

更新一下快照:

yarn test:update

可以看见同级目录下新增了一个__snapshots__文件夹,里面存放对应测试用例的快照文件。

生成的快照文件

再执行测试用例:

yarn test
通过测试用例

可以发现我们通过了测试用例。。。额,这里当然能通过,主要是后续我们进行迭代重构时,都会重新执行测试用例,与最近的一次快照进行比对,如果与快照不一致(结构发生了改变),那么相应的测试用例就无法通过。

对于快照测试,褒贬不一,这个例子也着实简单得很,甚至连扩展的 jest-dom提供的 matchers 都没用上,如何编写优秀的测试用例,我也是一个新手,只能说多看多写多尝试,概述推荐的文章很不错。

本文如有错误,恳请指正,共同交流学习。

To be Continued...