工具集
Jest
测试框架,用来跑测试用例
@testing-library
编写测试用例的库
@testing-library/react
专门用于测试 React 组件,必用,常用的 API 有 render
waitFor
screen
@testing-library/user-event
用于模拟用户事件,必用
@testing-library/jest-dom
扩展断言的,必用,例如:expect().toBeInTheDocument
msw
模拟接口请求,必用
测试三步骤
渲染组件、模拟用户事件、断言
问题记录
import { xx } from umi
xx is undefined
umi 内部的导入语句使用的 @@/xx,jest
无法识别,因此需要在 jest.config.js
中配置,代码如下:
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'^@@/(.*)$': '<rootDir>/src/.umi/$1',
},
在 umi 配置文件中使用了 define
配置全局变量,jest
无法读取
umi 中的 define
配置,本质上是使用的 webpack.DefinePlugin
插件实现,因此这是一个 DefinePlugin
与 jest
的问题,通过 jest.config.js
配置解决,代码如下:
globals: {
XXXX: 'xxxx',
}
组件使用到了 dva
,该如何初始化 store 数据,以进行测试
使用类似 react-redux 提供的方法,在自定义 render 中使用 <Provider store={store}>
没成功。
目前是在自定义 render 中直接实例化 dva,但是需要手动引入全部 model,不多说,上代码。
// 自定义 render
const customRender = (ui, { initialState, ...options }) => {
const app = dva({
initialState
});
app.model(require('../pages/policy/model').default);
app.model(require('../models/insurer').default);
app.router(() => ui);
const App = app.start();
const AllTheProviders = () => {
return (
<IntlProvider
locale={defaultLang}
messages={locales[defaultLang]}
>
<LocaleProvider currentLang={defaultLang}>
<App></App>
</LocaleProvider>
</IntlProvider>
)
}
return render(ui, { wrapper: AllTheProviders, ...options })
}
// index.test.js
test(xx, async () => {
customRender(<XX />, {
initilState: {
user: {
name: 'xy'
}
}
})
})
每个测试用例执行后,出现类似没有清空 document.body
的情况
比如我有三个测试用例,但第一个测试用例中,弹出了一个模态框,在接下来的测试用例中,模态框依然存在与 document.body
中,可以通过 screen.debug()
查看可见内容证明,极大影响了后面测试用例中的断言。
@testing-library/react
文件中说如果测试框架是 jest
,那么会在 afterEach
中自动调用 cleanup
方法。后面尝试手动调用 cleanup
也没成功,目前并不清楚问题出在哪里。
目前解决方法是在 afterEach
中调用 document.body.innerHTML = ''
清空每次测试用例中的渲染。我不清楚这是否会带来其他副作用,但至少解决了每个测试用例完毕后,渲染内容不清空的严重问题。
render
有一个container
参数,表示组件内容被渲染到哪个元素中,默认值是document.body
,并且模态框总是渲染到document.body
中,而不是指定的container
中的。或许这是cleanup
看起来没有生效的原因?
单元测试中常见需求介绍
jest 模拟本地文件模块
当本地文件模块中导出的内容无法在单元测试中运行,或者压根儿不是你想要的结果,那么可以选择手动模拟。方法如下:
首先在被模拟的文件所在目录中新建 __mocks__
文件夹,并放入被模拟的同名文件,同名文件中自定义导出内容,最后添加以下配置。
// jest.config.js
setupFilesAfterEnv: [
'<rootDir>/src/setupTests.js',
],
// setupTests.js
jest.mock(需导出的模块)
jest
中还有很多模拟相关内容,遇到了再添加。
模拟接口请求
@testing-library
文档示例中已经写得很请求了,照着抄就行了,nice。
异步测试
目前只遇到了请求接口异步的场景,也是相当常见的需求。
当模拟了接口请求后,需要获取相应内容时,不能直接通过 getByXXX
或者 queryByXXX
,因为它们都是同步的。
可以通过 waitFor
API 或者 findByXXX
,上代码:
// 该用例中,异步操作后,获取必定出现的内容
const ele = await screen.findByXXX();
// or
const ele = await waifFor(() => screen.getByXXX());
// 该用例中,异步操作后,获取不应该出现的内容
const ele = await waifFor(() => screen.queryByXXX());
expect(ele).not.toBeInTheDocument();
其实就是对
queryByXXX
findByXXX
getByXXX
waitFor
的合理应用
最后
欢迎指正