React Testing Library 常见测试场景
0. Install Packages
npm install --save-dev @testing-library/react @testing-library/jest-dom
1. 测试DOM元素
文件App.js
// App.js
import React from "react";
export const App = () => {
return <h1 data-testid="big-title">Hello React</h1>
}
文件 App.test.js
// App.test.js
import React from "react";
import { render, screen } from "@testing-library/react";
import { App } from "./App";
describe("App", () => {
test("renders App component", () => {
render(<App />);
// 不同的方法判断标题的Dom元素是否存在
expect(screen.getByRole("heading")).toHaveTextContent("Hello React");
expect(screen.getByRole("heading", { name: "Hello React" })).toBeInTheDocument();
expect(screen.getByText("Hello React")).toBeInTheDocument();
expect(screen.getByTestId("big-title")).toHaveTextContent("Hello React");
// 查询不存在的Dom元素
expect(screen.queryByTestId("another-big-title")).toBeNull();
});
});
testing-library提供了许多的方法来获取DOM元素,它们大致分为三类, 优先级是
- 查询(Accessible Queries)
- getByRole
- etByLabelText
- getByPlaceholderText
- getByText
- getByDisplayValue
- 语义查询(Semantic Queries)
- getByAltText
- getByTitle
- Test IDs
- getByTestId
2. 测试Event
文件App.js
// App.js
import React from "react";
export const App = ({ handleChange }) => {
return (
<div>
<label htmlFor="search">Search</label>
<input
id="search"
type="text"
value="Search"
onChange={handleChange}
/>
</div>
)
}
文件 App.test.js
// App.test.js
import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import { App } from "./App";
describe("App", () => {
test("renders App component", () => {
const mockSearchFunc = jest.fn();
// Render
render(<App handleChange={mockSearchFunc}/>);
// Act
fireEvent.change(screen.getByRole("textbox"));
// Asset
expect(mockSearchFunc.call.length).toBe(1);
});
});
测试组件的事件时,首先要mock事件(jest.fn()),然后通过screen的方法选中元素,由fireEvent来模拟事件的实现,最后进行断言。
fireEvent可以实现常见的事件,如change, click, focus等等。
3. 异步Event - await
文件App.js
// App.js
import React from "react";
const getUser = () => {
return Promise.resolve({ id: "1", name: "Robin" });
};
export const App = () => {
const [user, setUser] = React.useState(null);
React.useEffect(() => {
const loadUser = async () => {
const user = await getUser();
setUser(user);
};
loadUser();
}, []);
return (
<div>
{user ? <p>Signed in as {user.name}</p> : null}
</div>
);
}
文件 App.test.js
// App.test.js
import React from "react";
import { render, screen } from "@testing-library/react";
import { App } from "./App";
describe("App", () => {
test("renders App component", async () => {
// Render
render(<App />);
// At first, the dom is not exist
expect(screen.queryByText(/Signed in as/)).toBeNull();
// Later, when function exec, the dom is appeared
expect(await screen.findByText(/Signed in as/)).toBeInTheDocument();
});
});
find* 方法适合查找异步调用之后的Dom元素,结合await使用
4. 异步Event - 测试包含请求数据的组件
文件App.js
// App.js
import React from 'react';
import axios from 'axios';
const URL = 'http://xxx/api/v1/search';
export const App = () => {
const [stories, setStories] = React.useState([]);
const [error, setError] = React.useState(null);
async function handleFetch(event) {
let result;
try {
result = await axios.get(`${URL}?query=React`);
setStories(result.data.hits);
} catch (error) {
setError(error);
}
}
return (
<div>
<button type="button" onClick={handleFetch}>
Fetch Stories
</button>
{error && <span>Something went wrong ...</span>}
<ul>
{stories.map((story) => (
<li key={story.objectID}>
<a href={story.url}>{story.title}</a>
</li>
))}
</ul>
</div>
);
}
文件 App.test.js
// App.test.js
import React from 'react';
import axios from 'axios';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import App from './App';
jest.mock('axios');
describe('App', () => {
test('fetches stories from an API and displays them', async () => {
const stories = [
{ objectID: '1', title: 'Hello' },
{ objectID: '2', title: 'React' },
];
axios.get.mockImplementationOnce(() =>
Promise.resolve({ data: { hits: stories } })
);
render(<App />);
await userEvent.click(screen.getByRole('button'));
const items = await screen.findAllByRole('listitem');
expect(items).toHaveLength(2);
});
});
Mock axios的方法实现,通过await返回测试数据并进行assert