API
React Testing Library
不仅提供了 DOM Testing Library
提供的方法,而且还提供
了:
render
function render(
ui: React.ReactElement<any>,
options?: {
/* You won't often use this, expand below for docs on options */
},
): RenderResult
渲染组件到一个容器,然后将容器附加到 document.body
.
import {render} from '@testing-library/react'
render(<div />)
import {render} from '@testing-library/react'
import '@testing-library/jest-dom'
test('renders a message', () => {
const {asFragment, getByText} = render(<Greeting />)
expect(getByText('Hello, world!')).toBeInTheDocument()
expect(asFragment()).toMatchInlineSnapshot(`
<h1>Hello, World!</h1>
`)
})
render
选项
你通常不需要指定选项,但如果你想改变 render
的默认行为,下面是一些可用的选项,
你可以将它们作为第二个参数提供给 render
函数。
container
默认情况下,React Testing Library
会创建一个 div
容器,在它上面来渲染你的
React 组件,然后将 div
其附加到 document.body
。通过 container
选项你可以指
定自己想要的容器,但是指定后的容器不会自动附加到 document.body
上(需要手动附
加)。
举例:如果你在测试一个 tablebody
元素,这时我们就不能使用默认的 div
作为其容
器。在这种情况下,你可以指定一个 table
作为其渲染的 container
。
const table = document.createElement('table')
const {container} = render(<TableBody {...props} />, {
container: document.body.appendChild(table),
})
baseElement
如果指定了 container
,则默认为该容器,否则默认为 document.body
。这被用作查询
的基础元素,也是你使用 debug()
时打印的内容。
hydrate
如果 hydrate 设置为 true,将会使用 ReactDOM.hydrate 来渲染组件。 当你使用服务器端渲染,且用 ReactDOM.hydrate 来 mount 你的组件。
legacyRoot
默认情况下,我们会在支持并发功能的情况下进行渲染(即
ReactDOMClient.createRoot
)。然而,如果你正在处理一个需要像 React 17 那样渲染的传统应用(即
ReactDOM.render),那么你应该通过设置 legacyRoot: true
来启用这个选项。
wrapper
传递一个 React 组件作为 wrapper
选项,让它围绕内部元素进行渲染。这对于为常见的
数据提供者创建可重复使用的自定义渲染函数是最有用的。例子
见设置。
queries
需要绑定的查询。除非合并,否则将会覆盖 DOM Testing Library
中的默认设置。
// Example, a function to traverse table contents
import * as tableQueries from 'my-table-query-library'
import {queries} from '@testing-library/react'
const {getByRowColumn, getByText} = render(<MyTable />, {
// 合并
queries: {...queries, ...tableQueries},
})
如何使用工具函数来创建自定义的查询函数,请参考 helpers。
自定义查询还可以通过全局的方式来添加,请参考 自定义渲染指南。
render
返回结果
render
方法返回一个带有以下属性的对象:
...queries
render
最重要的功能就是会自动返回 DOM Testing Library 提
供的查询函数,这些查询函数的第一个参数绑定到 baseElement,其默认
值是 document.body
。
完整的查询列表,请参考 查询。
示例
const {getByLabelText, queryAllByTestId} = render(<Component />)
container
你渲染的 React 元素(使用 ReactDOM.render 渲染)的包含 DOM 节点。它是一个
div
。这是一个普通的 DOM 节点,所以你可以调用 container.querySelector
等来检
查子节点。
提示:要获得你的渲染元素的根元素,使用
container.firstChild
。注意:当根元素是一个 React Fragment 时,
container.firstChild
只能得到该片段的第一个子元素,而不是片段本身。
🚨 如果你发现自己使用
container
来查询已渲染的元素,那么你应该重新考虑,其他 的查询被设计成对你正在测试的组件的变化更有弹性。避免使用容器来查询元素!
baseElement
包含 DOM 节点,你的 React 元素在容器中被渲染。如果你不在 render
的选项中指定
baseElement
,它将默认为 document.body
。
当你想测试的组件在容器 div 之外渲染一些东西时,这很有用,例如,当你想快照测试你 的 portal 组件时,它直接在 body 中渲染它的 HTML。
注意:由
render
返回的查询是针对 baseElement 的,所以你可以使用查询来测试你 的门户组件,而不需要 baseElement。
debug
注意: 更推荐使用
screen.debug
来调试
console.log(prettyDOM(baseElement))
的快捷方式。
import React from 'react'
import {render} from '@testing-library/react'
const HelloWorld = () => <h1>Hello World</h1>
const {debug} = render(<HelloWorld />)
debug()
// <div>
// <h1>Hello World</h1>
// </div>
// 你也可以传递一个元素 you can also pass an element: debug(getByTestId('messages'))
// and you can pass all the same arguments to debug as you can
// to prettyDOM:
// const maxLengthToPrint = 10000
// debug(getByTestId('messages'), maxLengthToPrint, {highlight: false})
这是对 prettyDOM 的一个简单包装,它也是公开的,来自
DOM Testing Library
。
rerender
如果你测试正在进行 props 更新的组件,以确保道具被正确地更新,那可能会更好( 见指导原则部分)。也就是说,如果你想在测试中更新一个已 渲染的组件的 props,这个函数可以用来更新已渲染组件的 props。
import {render} from '@testing-library/react'
const {rerender} = render(<NumberDisplay number={1} />)
// re-render the same component with different props
rerender(<NumberDisplay number={2} />)
unmount
这将导致渲染后的组件被卸载。这对于测试当你的组件从页面上移走时发生的事情是很有用 的(比如测试你没有留下事件处理程序导致内存泄漏)。
这个方法是对
ReactDOM.unmountComponentAtNode
非常小的抽象
import {render} from '@testing-library/react'
const {container, unmount} = render(<Login />)
unmount()
// 组件已经被卸载同时: container.innerHTML === ''
asFragment
返回你所渲染的组件的 DocumentFragment
。如果你需要避免实时绑定并查看你的组件对
事件的反应,这可能很有用。
import React, {useState} from 'react'
import {render, fireEvent} from '@testing-library/react'
const TestComponent = () => {
const [count, setCounter] = useState(0)
return (
<button onClick={() => setCounter(count => count + 1)}>
Click to increase: {count}
</button>
)
}
const {getByText, asFragment} = render(<TestComponent />)
const firstRender = asFragment()
fireEvent.click(getByText(/Click to increase/))
// This will snapshot only the difference between the first render, and the
// state of the DOM after the click event.
// See https://github.com/jest-community/snapshot-diff
expect(firstRender).toMatchDiffSnapshot(asFragment())
cleanup
解除用render挂载的 React 树。
请注意,如果你使用的测试框架支持
afterEach
全局,并且它被注入到你的测试环境 中(如 mocha、Jest 和 Jasmine),这将自动完成。如果不是,你将需要在每次测试后 进行手动清理。
例如,如果你使用的是ava测试框架,那么你就需要像
这样使用 test.afterEach
hook:
import {cleanup, render} from '@testing-library/react'
import test from 'ava'
test.afterEach(cleanup)
test('renders into document', () => {
render(<div />)
// ...
})
// ... more tests ...
当你调用 render
时没有调用 cleanup
可能会导致内存泄漏和测试不 "idempotent"(
这可能会导致你的测试中出现难以调试的错误)。
act
这是一个围绕 react-dom/test-utils
act
函数的轻型封装器。如果你的 react 版本
支持 act
,它所做的就是将所有参数转发给 act
函数。出于一致性的考虑,建议使用
从 @testing-library/react
导入,而不是从 react-dom/test-utils
导入。
renderHook
这是一个方便的包装,围绕着具有自定义测试组件的 render
。这个 API 是从一个流行的
测试模式中产生的,对于发布 hooks 的库来说是非常有趣的。你应该更喜欢 render
,因
为一个自定义的测试组件会产生更多的可读性和健壮的测试,因为你想测试的东西并没有隐
藏在一个抽象概念后面。
function renderHook<Result, Props>(
render: (props: Props) => Result,
options?: RenderHookOptions<Props>,
): RenderHookResult<Result, Props>
示例:
import {renderHook} from '@testing-library/react'
test('returns logged in user', () => {
const {result} = renderHook(() => useLoggedInUser())
expect(result.current).toEqual({name: 'Alice'})
})
renderHook
选项
renderHook
选项 initialProps
声明第一次调用时传递给 render-callback 的 props。如果你在没有 props 的情况下调
用rerender
,这些 props 将不会被传递。
import {renderHook} from '@testing-library/react'
test('returns logged in user', () => {
const {result, rerender} = renderHook((props = {}) => props, {
initialProps: {name: 'Alice'},
})
expect(result.current).toEqual({name: 'Alice'})
rerender()
expect(result.current).toEqual({name: undefined})
})
注:将
renderHook
与wrapper
和initialProps
选项结合使用时,initialProps
不 会传递到wrapper
组件。要向wrapper
组件提供 props,请考虑以下解决方案:const createWrapper = (Wrapper, props) => {
return function CreatedWrapper({ children }) {
return <Wrapper {...props}>{children}</Wrapper>;
};
};
...
{
wrapper: createWrapper(Wrapper, { value: 'foo' }),
}
renderHook
选项 wrapper
renderHook
返回结果
renderHook
方法返回一个有几个属性的对象。
result
保存最近提交的 render-callback 的返回值的值:
import {renderHook} from '@testing-library/react'
const {result} = renderHook(() => {
const [name, setName] = useState('')
React.useEffect(() => {
setName('Alice')
}, [])
return name
})
expect(result.current).toBe('Alice')
注意,该值被保存在 result.current
中。可以把 result
看作是最近提交的值的
参考 。
rerender
用新的 props 渲染先前渲染的回调:
import {renderHook} from '@testing-library/react'
const {rerender} = renderHook(({name = 'Alice'} = {}) => name)
// re-render the same hook with different props
rerender({name: 'Bob'})