设置
React Testing Library
不需要使用任何配置。但是,你可以通过配置测试框架来减少一
些模板代码。接下来我们用 Jest 来演示配置,当然你也可以使用
任何其它框架 来类似配置 (React Testing Library 不要求你一
定使用 Jest)。
全局配置
在你的全局测试配置中添加选项,可以简化单个文件中测试的设置和拆分。
自定义渲染
定义一个自定义的渲染方法,包括全局上下文提供者、数据存储等东西,通常是很有用的。
为了使其在全局范围内可用,一种方法是定义一个实用的文件,从
React Testing Library
中重新导出一切。你可以在你所有的导入中用这个文件替换
React Testing Library。请看下面的方法,使你
的测试 util 文件在不使用相对路径的情况下可以访问。
下面的例子设置了使用 wrapper
选项来设置数据提供者给
render
函数。
- Javascript
- Typescript
- import { render, fireEvent } from '@testing-library/react';
+ import { render, fireEvent } from '../test-utils';
import React from 'react'
import {render} from '@testing-library/react'
import {ThemeProvider} from 'my-ui-lib'
import {TranslationProvider} from 'my-i18n-lib'
import defaultStrings from 'i18n/en-x-default'
const AllTheProviders = ({children}) => {
return (
<ThemeProvider theme="light">
<TranslationProvider messages={defaultStrings}>
{children}
</TranslationProvider>
</ThemeProvider>
)
}
const customRender = (ui, options) =>
render(ui, {wrapper: AllTheProviders, ...options})
// re-export everything
export * from '@testing-library/react'
// override render method
export {customRender as render}
- import { render, fireEvent } from '@testing-library/react';
+ import { render, fireEvent } from '../test-utils';
import React, {ReactElement} from 'react'
import {render, RenderOptions} from '@testing-library/react'
import {ThemeProvider} from 'my-ui-lib'
import {TranslationProvider} from 'my-i18n-lib'
import defaultStrings from 'i18n/en-x-default'
const AllTheProviders = ({children}: {children: React.ReactNode}) => {
return (
<ThemeProvider theme="light">
<TranslationProvider messages={defaultStrings}>
{children}
</TranslationProvider>
</ThemeProvider>
)
}
const customRender = (
ui: ReactElement,
options?: Omit<RenderOptions, 'wrapper'>,
) => render(ui, {wrapper: AllTheProviders, ...options})
export * from '@testing-library/react'
export {customRender as render}
注意
低于 v7 的 Babel 在试图覆盖上述例子中的命名导出时,会出现错误。参 见#169和 下面的解决方法。
Babel 6的解决方法
你可以使用 CommonJS 模块而不是 ES 模块,这在 Node 中应该是可行的:
const rtl = require('@testing-library/react')
const customRender = (ui, options) =>
rtl.render(ui, {
myDefaultOption: 'something',
...options,
})
module.exports = {
...rtl,
render: customRender,
}
添加自定义查询
注意
一般来说,你不应该需要为 react-testing-library 创建自定义查询。如果你使用它, 你应该考虑你的新查询是否鼓励你以用户为中心的方式进行测试,而不是测试实现细节。
你可以按照 自定义查询 文档中的描述
定义你自己的自定义查询,或者通过
buildQueries
辅助工
具。然后你可以在任何渲染调用中使用 queries
选项来使用它们。为了使自定义查询在
全局范围内可用,你可以把它们添加到你的自定义渲染方法中,如下图所示:
在下面的例子中,创建了一组新的查询变体,用于通过 data-cy
获取元素,这是
Cypress.io
文档中提到的一个 "测试 ID "约定。
- JavaScript
- TypeScript
import {queryHelpers, buildQueries} from '@testing-library/react'
// The queryAllByAttribute is a shortcut for attribute-based matchers
// You can also use document.querySelector or a combination of existing
// testing library utilities to find matching nodes for your query
const queryAllByDataCy = (...args) =>
queryHelpers.queryAllByAttribute('data-cy', ...args)
const getMultipleError = (c, dataCyValue) =>
`Found multiple elements with the data-cy attribute of: ${dataCyValue}`
const getMissingError = (c, dataCyValue) =>
`Unable to find an element with the data-cy attribute of: ${dataCyValue}`
const [
queryByDataCy,
getAllByDataCy,
getByDataCy,
findAllByDataCy,
findByDataCy,
] = buildQueries(queryAllByDataCy, getMultipleError, getMissingError)
export {
queryByDataCy,
queryAllByDataCy,
getByDataCy,
getAllByDataCy,
findAllByDataCy,
findByDataCy,
}
import {
queryHelpers,
buildQueries,
Matcher,
MatcherOptions,
} from '@testing-library/react'
// The queryAllByAttribute is a shortcut for attribute-based matchers
// You can also use document.querySelector or a combination of existing
// testing library utilities to find matching nodes for your query
const queryAllByDataCy = (
container: HTMLElement,
id: Matcher,
options?: MatcherOptions | undefined,
) => queryHelpers.queryAllByAttribute('data-cy', container, id, options)
const getMultipleError = (c, dataCyValue) =>
`Found multiple elements with the data-cy attribute of: ${dataCyValue}`
const getMissingError = (c, dataCyValue) =>
`Unable to find an element with the data-cy attribute of: ${dataCyValue}`
const [
queryByDataCy,
getAllByDataCy,
getByDataCy,
findAllByDataCy,
findByDataCy,
] = buildQueries(queryAllByDataCy, getMultipleError, getMissingError)
export {
queryByDataCy,
queryAllByDataCy,
getByDataCy,
getAllByDataCy,
findAllByDataCy,
findByDataCy,
}
然后,你可以通过传递一个queries
选项,通过渲染函数覆
盖和追加新的查询。
如果你想在全局范围内添加自定义查询,你可以通过定义一个自定义的 render
,
screen
和 within
方法来实现:
- Javascript
- Typescript
import {render, queries, within} from '@testing-library/react'
import * as customQueries from './custom-queries'
const allQueries = {
...queries,
...customQueries,
}
const customScreen = within(document.body, allQueries)
const customWithin = element => within(element, allQueries)
const customRender = (ui, options) =>
render(ui, {queries: allQueries, ...options})
// re-export everything
export * from '@testing-library/react'
// override render method
export {customScreen as screen, customWithin as within, customRender as render}
import {render, queries, within, RenderOptions} from '@testing-library/react'
import * as customQueries from './custom-queries'
import {ReactElement} from 'react'
const allQueries = {
...queries,
...customQueries,
}
const customScreen = within(document.body, allQueries)
const customWithin = (element: ReactElement) => within(element, allQueries)
const customRender = (
ui: ReactElement,
options?: Omit<RenderOptions, 'queries'>,
) => render(ui, {queries: allQueries, ...options})
export * from '@testing-library/react'
export {customScreen as screen, customWithin as within, customRender as render}
然后你可以像使用其他查询一样使用你的自定义查询:
const {getByDataCy} = render(<Component />)
expect(getByDataCy('my-component')).toHaveTextContent('Hello')
使用 Test Utils 来配置 Jest
为了使你的自定义测试文件能够在你的 Jest 测试文件中访问,而不使用相对导入
(.../.../test-utils
),将包含该文件的文件夹添加到 Jest moduleDirectories
选
项中。
这将使 test-utils 目录下的所有 .js
文件都可以导入,而不需要../
。
- import { render, fireEvent } from '../test-utils';
+ import { render, fireEvent } from 'test-utils';
module.exports = {
moduleDirectories: [
'node_modules',
+ // add the directory with the test-utils.js file, for example:
+ 'utils', // a utility folder
+ __dirname, // the root directory
],
// ... other options ...
}
如果你使用 TypeScript,把它合并到你的 tsconfig.json
。如果你使用的是不带
TypeScript 的 Create React App,请将其保存到jsconfig.json
中。
{
"compilerOptions": {
"baseUrl": "src",
"paths": {
"test-utils": ["./utils/test-utils"]
}
}
}
Jest 28
如果你使用 Jest 28 或更高版本,现在必须单独安装 jest-environment-jsdom 包。
- npm
- Yarn
npm install --save-dev jest-environment-jsdom
yarn add --dev jest-environment-jsdom
jsdom
也不再是默认环境了。你可以通过编辑 jest.config.js
来全局启用 jsdom
。
module.exports = {
+ testEnvironment: 'jsdom',
// ... other options ...
}
或者,如果你只在某些测试中需要 jsdom
,你可以在需要时使用
docblocks 来启用
它:
/**
* @jest-environment jsdom
*/
Jest 27
如果你使用的是 Jest 的最新版本(27),jsdom
就不再是默认环境了。你可以通过编辑
jest.config.js
来全局启用 jsdom
。
module.exports = {
+ testEnvironment: 'jest-environment-jsdom',
// ... other options ...
}
或者,如果你只在某些测试中需要 jsdom
,你可以在需要时使用
docblocks 来启用
它:
/**
* @jest-environment jsdom
*/
Jest 24 (或更低) and 默认
如果你使用 Jest 测试框架 24 版或更低的默认配置,建议使用
jest-environment-jsdom-fifteen
包,因为 Jest 使用的 jsdom 环境版本缺少一些功能
和修正,是 React Testing Library 所需要的。
首先,安装 jest-environment-jsdom-fifteen
。
- npm
- Yarn
npm install --save-dev jest-environment-jsdom-fifteen
yarn add --dev jest-environment-jsdom-fifteen
然后指定 jest-environment-jsdom-fifteen
作为 testEnvironment
。
module.exports = {
+ testEnvironment: 'jest-environment-jsdom-fifteen',
// ... other options ...
}
不结合 Jest 使用
如果你在浏览器中与 webpack(或类似)打包运行你的测试,那么
React Testing Library
应该开箱即用。然而,大多数使用 React Testing Library 的
人都在使用 Jest 测试框架,并将 testEnvironment
设置为
jest-environment-jsdom
(这是 Jest 26 及以前的默认配置)。
jsdom
是 DOM 和浏览器 API 的纯 JavaScript 实现,在 Node 中运行。如果你没有使用
Jest,而你想在 Node 中运行你的测试,那么你必须自己安装 jsdom。还有一个叫
global-jsdom
的包,可以用来设置全局环境来模拟浏览器 API。
首先,安装 jsdom
和 global-jsdom
。
- npm
- Yarn
npm install --save-dev jsdom global-jsdom
yarn add --dev jsdom global-jsdom
使用 mocha,测试命令看起来是这样的:
mocha --require global-jsdom/register
跳过自动清理
如果你使用的测试框架支持 afterEach 全局(如 mocha、Jest 和 Jasmine),每次测试后
都会自动调用清理清理
。然而,你可以通过设置
RTL_SKIP_AUTO_CLEANUP
环境变量为 'true' 来跳过自动清理。你可以使用
cross-env
完成这件事:
cross-env RTL_SKIP_AUTO_CLEANUP=true jest
为了更简单,你也可以简单地导入
@testing-library/react/dont-cleanup-after-each
,这将做同样的事情。只要确保你在
导入 @testing-library/react
之前做这个。你可以用 Jest 的 setupFiles
配置来做
这个:
{
// ... other jest config
setupFiles: ['@testing-library/react/dont-cleanup-after-each']
}
或者使用 mocha 的 -r
标记:
mocha -r @testing-library/react/dont-cleanup-after-each
另外,你可以在所有你不希望 cleanup
运行的测试中导入
@testing-library/react/pure
,这样 afterEach
就不会被自动设置。
mocha 观察模式下的自动清理
当在观察模式下使用 Mocha 时,全局注册的清理工作只在每个测试后的第一次运行。因此 ,随后的运行很可能会失败,出现 TestingLibraryElementError。发现多个元素的错误。
要在 Mocha 的观察模式下启用自动清理,请添加一个
root hook。创建一个
mocha-watch-cleanup-after-each.js
文件,内容如下:
const {cleanup} = require('@testing-library/react')
exports.mochaHooks = {
afterEach() {
cleanup()
},
}
并使用 mocha 的 -r
标志来注册它:
mocha -r ./mocha-watch-cleanup-after-each.js