设置
React Testing Library
不需要任何配置即可使用。但是,在配置测试框架时,您可以做一些事情来减少一些样板代码。在这些文档中,我们将演示如何配置 Jest,但您应该能够对 任何测试框架 做类似的事情(React Testing Library 不需要您使用 Jest)。
全局配置
将选项添加到全局测试配置可以简化单个文件中测试的设置和拆卸。
自定义渲染
定义一个自定义渲染方法通常很有用,其中包括全局上下文提供者、数据存储等。为了使它在全局范围内可用,一种方法是定义一个实用程序文件,该文件从 React Testing Library
重新导出所有内容。您可以在所有导入中将 React Testing Library 替换为此文件。有关使您的测试实用程序文件在不使用相对路径的情况下可访问的方法,请参见 下面。
下面的示例使用 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}
注意
低于 7 的 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(或更低版本)和默认值
如果您使用的是 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
跳过自动清理
Cleanup
默认情况下在每次测试后由您使用的测试框架自动调用,如果该框架支持 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 --require @testing-library/react/dont-cleanup-after-each
或者,您可以在所有不希望 cleanup
运行的测试中导入 @testing-library/react/pure
,这样 afterEach
不会自动设置。
在 Mocha 的 watch 模式下进行自动清理
在 watch 模式下使用 Mocha 时,全局注册的清理仅在每次测试后第一次运行。因此,后续运行很可能会失败,并出现TestingLibraryElementError: Found multiple elements 错误。
要在 Mocha 的 watch 模式下启用自动清理,请添加清理 root hook。创建一个具有以下内容的 mocha-watch-cleanup-after-each.js
文件
const {cleanup} = require('@testing-library/react')
exports.mochaHooks = {
afterEach() {
cleanup()
},
}
并使用 mocha 的 -r
标志注册它。
mocha --require ./mocha-watch-cleanup-after-each.js