0


Jest进阶知识:React组件的单元测试

在现代前端开发中,组件是构建应用程序的基本单元。一个组件不仅拥有完整的功能,还能极大地提高代码的复用性。因此,在进行单元测试时,对重要组件进行测试是必不可少的。

Testing Library

Testing Library 是一个专门用于测试 Web 组件的工具库,其设计理念是“测试组件的行为而不是实现细节”。通过 Testing Library 提供的一系列 API,可以模拟浏览器中的用户交互方式,使测试更加贴近真实使用场景。

Jest 与 Testing Library 的关系

  • Jest:一个完整的测试框架,提供了匹配器、mock 库、断言工具等,旨在提供一个全面的测试工具链。
  • Testing Library:一个测试工具库,专注于测试组件的行为。它可以与各种框架结合使用,提供了一组用于测试 React 组件的工具,如 renderscreenfireEvent 等。

常用的 Testing Library 扩展库

  • @testing-library/react:提供了一组用于测试 React 组件的工具,如 renderscreenfireEvent
  • @testing-library/jest-dom:提供了一组 Jest 断言方法,用于测试 DOM 元素的状态和行为,如 toBeInTheDocumenttoHaveTextContent 等。
  • @testing-library/user-event:提供了一组用于模拟用户行为的工具,如 typeclicktab 等。

核心 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";functionHiddenMessage({ children }){const[isShow, setIsShow]=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>);}exportdefault 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";functionLogin(){const[state, setState]= React.useReducer((s, a)=>({...s,...a }),{ resolved:false, loading:false, error:null});functionhandleSubmit(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>);}exportdefault 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)=>{returnres(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)=>{returnres(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 组件进行单元测试。通过对组件的行为进行测试,可以确保组件在不同情况下的表现符合预期,从而提高代码的可靠性和可维护性。


本文转载自: https://blog.csdn.net/weixin_53961451/article/details/143494194
版权归原作者 王解 所有, 如有侵权,请联系我们删除。

“Jest进阶知识:React组件的单元测试”的评论:

还没有评论