umi-test 单元测试使用记录
arvinxx opened this issue · 13 comments
在这里记录下 umi-test 单测的一些使用情况。完备之后会提个 PR 给官方文档。
umi-test Cli
目前 umi test
命令支持两个参数选项 --watch
和 --coverage
,均为jest
自带的方法。
个人希望之后能够提供匹配测试文件路径的选项,便于进行单个测试文件的测试。#428
如果需要进行单个文件的单元测试
使用 umi test --watch
后,按 p
进入文件匹配模式,可以针对单个文件进行测试。
Css Modules Mock
大多数情况下我们都会使用 Css Modules 进行样式命名,但这就会导致进行测试时不清楚随机生成的 className 到底是什么。为了解决这个问题,umi-test 引入了 identity-obj-proxy
库来 mock Css Modules 的 className。在实际使用时只需要使用样式文件中的 css 类名即可。
// App Component
import React, { Component } from 'react';
import styles from './App.css';
export default class App extends Component {
render() {
return (
<h1 className={styles.hello}>Hello, world!</h1>
);
}
}
// test
import React from 'react';
import { shallow } from 'enzyme';
import App from './App';
it('App Component should be render', () => {
expect(wrapper.find('.hello').length).toEqual(1);
}
// test will pass
如果需要进行 config 覆盖,可以在 jest.config.js
中配置 moduleNameMapper
参数。
module.exports={
//...
moduleNameMapper: {
'\\.(css|less|sass|scss)$': require.resolve('your-package'),
},
//...
}
umi-test 默认配置
umi-test 默认配置覆盖了日常 90% 以上的需求,包括 Typescript 支持、转义 jsx/tsx、Css Modules Mock、测试覆盖率等。
const config = {
rootDir: process.cwd(),
setupFiles: [
require.resolve('./shim.js'),
require.resolve('./setupTests.js'),
],
transform: {
'\\.jsx?$': require.resolve('./transformers/jsTransformer'),
'\\.tsx?$': require.resolve('./transformers/tsTransformer'),
},
testMatch: ['**/?(*.)(spec|test|e2e).(j|t)s?(x)'],
moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx'],
setupTestFrameworkScriptFile: require.resolve('./jasmine'),
moduleNameMapper: {
'\\.(css|less|sass|scss)$': require.resolve('identity-obj-proxy'),
},
globals: {
'ts-jest': {
useBabelrc: true,
},
},
...(coverage // 判断是否有测试覆盖
? {
collectCoverageFrom: [
'pages/**/*.{ts,tsx,js,jsx}',
'src/**/*.{ts,tsx,js,jsx}',
'!**/*.d.ts',
],
collectCoverage: true,
coveragePathIgnorePatterns: [
`/${pagesPath}/.${libraryName}/`,
`/${pagesPath}/.${libraryName}-production/`,
],
}
: {}),
...(userJestConfig || {}), // 判断是否有自定义配置
};
jest 自定义配置
如果需要进行自定义 jest 配置,需要在根目录下添加 jest.config.js
文件。config 中的配置将会覆盖默认配置。
Dva 组件测试方式
被 dva connect 的 React 组件 使用 WrappedComponent
属性获取包裹后的组件。
Ref : 测试 dva 包装组件
import React from 'react';
import { shallow } from 'enzyme';
import Dashboard from './Dashboard';
it('renders Dashboard', () => {
// 使用包装后的组件
const wrapper = shallow(
<Dashboard.WrappedComponent user={{ list: [] }} />
);
expect(wrapper.find('Table').props().dataSource).toEqual([]);
});
测试文件中引入图片文件报错
如果在 js 或 ts 文件中引入了图片文件,在测试时会报错
\path\to\img\1.jpg:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,glo
bal,jest){����
^
SyntaxError: Invalid or unexpected token
> 1 | import photo1 from '../assets/photos/1.jpg';
2 |
原因是 umi-test 的配置中没有mock掉图片资源,因此 jest 加载会出错。
解决方案是在项目目录下新建 jest.config.js
,添加一条 moduleNameMapper
的配置项。该配置项用于 Mock 相应的文件资源。
{
"moduleNameMapper": {
'\\.(css|less|sass|scss)$': require.resolve('identity-obj-proxy'), // 原有配置,不能改动。
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
},
}
因为 umi-test 的 config 配置会覆盖默认配置,因此需要保留原有的第一条配置,不然就无法进行样式的Mock。
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$"
该项为匹配各种格式的资源文件,"<rootDir>/__mocks__/fileMock.js"
指用 ./__mocks__/fileMock.js
文件进行替换。
因此,我们需要在根目录下新建 __mocks__
文件夹,增加一个 fileMock.js
文件,内容随意填写,只要有输出即可。最简单的就是
// fileMock.js
export default {};
当然,路径与文件名都可以自行替换。
Path Alias 在 Jest 中的使用
在 Webpack 中设置过的 path alias 在 jest 中是无法识别的。因此我们需要为 jest 添加 path alias 设置。
例如在 webpackrc 中,alias 设置如下:
alias: {
'@/components': resolve(__dirname, './src/components'),
'@/utils': resolve(__dirname, './src/utils'),
},
PS: 以 @ 开头主要是为了与 node_modules 区分开来,大家可以根据自己的喜好进行命名
在 jest 的配置文件 jest.config.js
中,在 moduleNameMapper
这一栏中添加对应的设置:
moduleNameMapper: {
'@/components': '<rootDir>/src/components',
'@/utils': '<rootDir>/src/utils',
},
PS:即项目根目录
如此 jest 就可以识别 Path Alias 了。
"moduleNameMapper": {
"@(.*)$": "<rootDir>/src/$1"
}
这么写就行,不用挨个配置
Dva 组件测试方式
被 dva connect 的 React 组件 使用
WrappedComponent
属性获取包裹后的组件。Ref : 测试 dva 包装组件
import React from 'react'; import { shallow } from 'enzyme'; import Dashboard from './Dashboard'; it('renders Dashboard', () => { // 使用包装后的组件 const wrapper = shallow( <Dashboard.WrappedComponent user={{ list: [] }} /> ); expect(wrapper.find('Table').props().dataSource).toEqual([]); });
组件中如果在componentDidMount使用dispatch发起请求的话,test的时候会报错 TypeError: dispatch is not a function
,该如何解决呢
问下使用了 @umijs/plugin-model 的组件怎么测试了?
Dva 组件测试方式
被 dva connect 的 React 组件 使用
WrappedComponent
属性获取包裹后的组件。Ref : 测试 dva 包装组件
import React from 'react'; import { shallow } from 'enzyme'; import Dashboard from './Dashboard'; it('renders Dashboard', () => { // 使用包装后的组件 const wrapper = shallow( <Dashboard.WrappedComponent user={{ list: [] }} /> ); expect(wrapper.find('Table').props().dataSource).toEqual([]); });
如果用jest怎么获取wrapper component
umi-test 默认配置
umi-test 默认配置覆盖了日常 90% 以上的需求,包括 Typescript 支持、转义 jsx/tsx、Css Modules Mock、测试覆盖率等。
const config = { rootDir: process.cwd(), setupFiles: [ require.resolve('./shim.js'), require.resolve('./setupTests.js'), ], transform: { '\\.jsx?$': require.resolve('./transformers/jsTransformer'), '\\.tsx?$': require.resolve('./transformers/tsTransformer'), }, testMatch: ['**/?(*.)(spec|test|e2e).(j|t)s?(x)'], moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx'], setupTestFrameworkScriptFile: require.resolve('./jasmine'), moduleNameMapper: { '\\.(css|less|sass|scss)$': require.resolve('identity-obj-proxy'), }, globals: { 'ts-jest': { useBabelrc: true, }, }, ...(coverage // 判断是否有测试覆盖 ? { collectCoverageFrom: [ 'pages/**/*.{ts,tsx,js,jsx}', 'src/**/*.{ts,tsx,js,jsx}', '!**/*.d.ts', ], collectCoverage: true, coveragePathIgnorePatterns: [ `/${pagesPath}/.${libraryName}/`, `/${pagesPath}/.${libraryName}-production/`, ], } : {}), ...(userJestConfig || {}), // 判断是否有自定义配置 };
配置文件地址 ./test/src/createDefaultConfig/createDefaultConfig.ts