Jest进阶知识:React组件的单元测试
在当代前端开发中,组件是构建应用步伐的根本单元。一个组件不仅拥有完整的功能,还能极大地进步代码的复用性。因此,在进行单元测试时,对重要组件进行测试是必不可少的。Testing Library
Testing Library 是一个专门用于测试 Web 组件的工具库,其计划理念是“测试组件的行为而不是实现细节”。通过 Testing Library 提供的一系列 API,可以模拟欣赏器中的用户交互方式,使测试更加贴近真实利用场景。
Jest 与 Testing Library 的关系
[*]Jest:一个完整的测试框架,提供了匹配器、mock 库、断言工具等,旨在提供一个全面的测试工具链。
[*]Testing Library:一个测试工具库,专注于测试组件的行为。它可以与各种框架结合利用,提供了一组用于测试 React 组件的工具,如 render、screen 和 fireEvent 等。
常用的 Testing Library 扩展库
[*]@testing-library/react:提供了一组用于测试 React 组件的工具,如 render、screen 和 fireEvent。
[*]@testing-library/jest-dom:提供了一组 Jest 断言方法,用于测试 DOM 元素的状态和行为,如 toBeInTheDocument、toHaveTextContent 等。
[*]@testing-library/user-event:提供了一组用于模拟用户行为的工具,如 type、click 和 tab 等。
焦点 API
render 方法
render 方法吸收一个组件作为参数,将其渲染为 DOM 元素,并返回一个包含重要属性的对象:
[*]container:渲染后的 DOM 元素,可用于模拟用户行为或进行断言验证。
[*]baseElement:整个文档的根元素 <html>。
[*]asFragment:将渲染后的 DOM 元素转换为 DocumentFragment 对象,便于进行快照测试。
[*]debug:在控制台输出渲染后的 DOM 元素的 HTML 布局,便于调试。
screen 对象
screen 对象封装了一系列常用的 DOM 查询和操作函数:
[*]getByLabelText:根据 <label> 元素的 for 属性或内部文本,获取与之关联的表单元素。
[*]getByText:根据文本内容获取元素。
[*]getByRole:根据 role 属性获取元素。
[*]getByPlaceholderText:根据 placeholder 属性获取表单元素。
[*]getByTestId:根据 data-testid 属性获取元素。
[*]queryBy*:类似于 getBy*,但当元素不存在时返回 null 而不是抛出非常。
测试组件示例
示例一:隐蔽消息组件
import { useState } from "react";
function HiddenMessage({ children }) {
const = useState(false);
return (
<div>
<label htmlFor="toggle">显示信息</label>
<input
type="checkbox"
name="toggle"
id="toggle"
checked={isShow}
onChange={(e) => setIsShow(e.target.checked)}
/>
{isShow ? children : null}
</div>
);
}
export default HiddenMessage;
该组件吸收一个子组件,并根据复选框的状态决定是否表现子组件。以下是对应的测试代码:
import { render, screen, fireEvent } from "@testing-library/react";
import HiddenMessage from "../HiddenMessage";
test("能够被勾选,功能正常", () => {
const testMessage = "这是一条测试信息";
render(<HiddenMessage>{testMessage}</HiddenMessage>);
// 初始状态下,信息不应显示
expect(screen.queryByText(testMessage)).toBeNull();
// 模拟点击复选框
fireEvent.click(screen.getByLabelText("显示信息"));
// 信息应显示
expect(screen.getByText(testMessage)).toBeInTheDocument();
});
示例二:登录组件
import * as React from "react";
function Login() {
const = React.useReducer(
(s, a) => ({ ...s, ...a }),
{ resolved: false, loading: false, error: null }
);
function handleSubmit(event) {
event.preventDefault();
const { usernameInput, passwordInput } = event.target.elements;
setState({ loading: true, resolved: false, error: null });
window
.fetch("/api/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
username: usernameInput.value,
password: passwordInput.value,
}),
})
.then((r) =>
r.json().then((data) => (r.ok ? data : Promise.reject(data)))
)
.then(
(user) => {
setState({ loading: false, resolved: true, error: null });
window.localStorage.setItem("token", user.token);
},
(error) => {
setState({ loading: false, resolved: false, error: error.message });
}
);
}
return (
<div>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="usernameInput">Username</label>
<input id="usernameInput" />
</div>
<div>
<label htmlFor="passwordInput">Password</label>
<input id="passwordInput" type="password" />
</div>
<button type="submit">
Submit{state.loading ? "..." : null}
</button>
</form>
{state.error ? <div role="alert">{state.error}</div> : null}
{state.resolved ? (
<div role="alert">Congrats! You're signed in!</div>
) : null}
</div>
);
}
export default Login;
该组件处理用户的登录哀求,根据哀求结果表现不同的信息。以下是对应的测试代码:
import { rest } from "msw";
import { setupServer } from "msw/node";
import { render, screen, fireEvent } from "@testing-library/react";
import Login from "../Login";
const fakeUserRes = { token: "fake_user_token" };
const server = setupServer(
rest.post("/api/login", (req, res, ctx) => {
return res(ctx.json(fakeUserRes));
})
);
// 启动服务器
beforeAll(() => server.listen());
// 关闭服务器
afterAll(() => server.close());
// 每个测试用例完成后重置服务器状态
afterEach(() => {
server.resetHandlers();
window.localStorage.removeItem("token");
});
test("测试请求成功", async () => {
render(<Login />);
fireEvent.change(screen.getByLabelText(/Username/i), {
target: { value: "xiejie" },
});
fireEvent.change(screen.getByLabelText(/Password/i), {
target: { value: "123456" },
});
fireEvent.click(screen.getByText("Submit"));
expect(await screen.findByRole("alert")).toHaveTextContent(/Congrats/i);
expect(window.localStorage.getItem("token")).toEqual(fakeUserRes.token);
});
test("测试请求失败", async () => {
server.use(
rest.post("/api/login", (req, res, ctx) => {
return res(ctx.status(500), ctx.json({ message: "服务器内部出错" }));
})
);
render(<Login />);
fireEvent.change(screen.getByLabelText(/Username/i), {
target: { value: "xiejie" },
});
fireEvent.change(screen.getByLabelText(/Password/i), {
target: { value: "123456" },
});
fireEvent.click(screen.getByText("Submit"));
expect(await screen.findByRole("alert")).toHaveTextContent(/服务器内部出错/i);
expect(window.localStorage.getItem("token")).toBeNull();
});
结论
通过本文的先容,我们了解了怎样利用 Testing Library 和 Jest 对 React 组件进行单元测试。通过对组件的行为进行测试,可以确保组件在不同情况下的表现符合预期,从而进步代码的可靠性和可维护性。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]