此外,如果你的组件包含异步任务,比如在页面加载的一开始先请求用户信息,那么上面的测试代码就会提示下面的错误信息:
"Warning: An update to App inside a test was not wrapped in act(...).".这代表这里有异步任务需要我们等待,需要先等异步人物执行完毕以后再进行其它的操作
describe('App', () => {
test('renders App component', async () => {
render(<App />);
// wait for the user to resolve// needs only be used in our special caseawait screen.findByText(/Signed in as/);
screen.debug();
fireEvent.change(screen.getByRole('textbox'), {
target: { value: 'JavaScript' },
screen.debug();
然后我们再针对input键入事件前后页面变化进行断言
describe('App', () => {
test('renders App component', async () => {
render(<App />);
// wait for the user to resolve// needs only be used in our special caseawait screen.findByText(/Signed in as/);
expect(screen.queryByText(/Searches for JavaScript/)).toBeNull();
fireEvent.change(screen.getByRole('textbox'), {
target: { value: 'JavaScript' },
expect(screen.getByText(/Searches for JavaScript/)).toBeInTheDocument();
importReactfrom'react';
import axios from'axios';
import { render, screen } from'@testing-library/react';
import userEvent from'@testing-library/user-event';
importAppfrom'./App';
jest.mock('axios');
describe('App', () => {
test('fetches stories from an API and displays them', async () => {
test('fetches stories from an API and fails', async () => {
axios.get.mockImplementationOnce(() =>Promise.reject(newError())
render(<App />);
await userEvent.click(screen.getByRole('button'));
const message = await screen.findByText(/Something went wrong/);
expect(message).toBeInTheDocument();
React Router
待测试组件:
// app.js
importReactfrom'react'import { Link, Route, Switch, useLocation } from'react-router-dom'constAbout = () => <div>You are on the about page</div>constHome = () => <div>You are home</div>constNoMatch = () => <div>No match</div>exportconstLocationDisplay = () => {
const location = useLocation()
return<divdata-testid="location-display">{location.pathname}</div>exportconstApp = () => (
<Linkto="/">Home</Link><Linkto="/about">About</Link><Switch><Routeexactpath="/"><Home /></Route><Routepath="/about"><About /></Route><Route><NoMatch /></Route></Switch><LocationDisplay /></div>
测试代码:
// app.test.jsimport { render, screen } from'@testing-library/react'import userEvent from'@testing-library/user-event'import { createMemoryHistory } from'history'importReactfrom'react'import { Router } from'react-router-dom'import'@testing-library/jest-dom/extend-expect'import { App, LocationDisplay } from'./app'test('full app rendering/navigating', () => {
const history = createMemoryHistory()
render(
<Routerhistory={history}><App /></Router>// verify page content for expected route// often you'd use a data-testid or role query, but this is also possibleexpect(screen.getByText(/you are home/i)).toBeInTheDocument()
const leftClick = { button: 0 }
userEvent.click(screen.getByText(/about/i), leftClick)
// check that the content changed to the new pageexpect(screen.getByText(/you are on the about page/i)).toBeInTheDocument()
test('landing on a bad page', () => {
const history = createMemoryHistory()
history.push('/some/bad/route')
render(
<Routerhistory={history}><App /></Router>expect(screen.getByText(/no match/i)).toBeInTheDocument()
test('rendering a component that uses useLocation', () => {
const history = createMemoryHistory()
const route = '/some-route'
history.push(route)
render(
<Routerhistory={history}><LocationDisplay /></Router>expect(screen.getByTestId('location-display')).toHaveTextContent(route)
React Redux
待测试组件:
import { connect } from'react-redux'constApp = props => {
return<div>{props.user}</div>constmapStateToProps = state => {
return state
exportdefaultconnect(mapStateToProps)(App)
// bad// assuming you've got this DOM to work with:// <label>Username</label><input data-testid="username" />
screen.getByTestId('username');;
// good// change the DOM to be accessible by associating the label and setting the type// <label for="username">Username</label><input id="username" type="text" />
screen.getByRole('textbox', {name: /username/i});
// badconst {container} = render(<Example />);
const button = container.querySelector('.btn-primary');
expect(button).toHaveTextContent(/click me/i);
// goodrender(<Example />)
screen.getByRole('button', {name: /click me/i});
// bad
screen.getByTestId('submit-button');
// good
screen.getByRole('button', {name: /submit/i});
Not using @testing-library/user-event
建议:Use @testing-library/user-event over fireEvent where possible.
它可以用于查询处于accessibility tree中的所有元素,并且可以通过name选项过滤返回的元素。它应该是你查询的首选项。大多数情况下,它都会带着name选项一起使用,就像这样:getByRole('button', {name: /submit/i})。这里是Roles的列表可供参考Roles list