【前端测试与集成】React Testing Library

React Testing Library 是测试 React 组件的测试库,与jest配合使用。功能类比于 Airbnb 的 Enzyme


github参考代码

1. 测试组件渲染

// specs/1.render.spec.jsx
import * as React from "react";
import * as ReactDOM from "react-dom";

import { App } from "../src/App";

test.skip("renders the correct content", () => {
  const root = document.createElement("div");
  ReactDOM.render(<App />, root);

  expect(root.querySelector("h1").textContent).toBe("TODOs");
  expect(root.querySelector("label").textContent).toBe(
    "What needs to be done?"
  );
  expect(root.querySelector("button").textContent).toBe("Add #1");
})

2. 测试获取DOM内容

// specs/2.queryDom.spec.jsx
import * as React from "react";
import * as ReactDOM from "react-dom";
import { getQueriesForElement } from "@testing-library/dom";

import { App } from "../src/App";

test.skip("renders the correct content", () => {
  const root = document.createElement("div");
  ReactDOM.render(<App />, root);

  const { getByText, getByLabelText } = getQueriesForElement(root);

  getByText("TODOs");
  getByLabelText("What needs to be done?");
  getByText("Add #1");
});

3. 使用react库测试渲染

  • render(<App />) 渲染组件
  • screen.debug() 在命令行上运行测试后,输出App组件的HTML
// specs/3.reactLib.spec.jsx
import { render, screen} from "@testing-library/react";

test('renders App component', () => {
  render(<App />); // 渲染组件

  screen.debug(); // 在命令行上运行测试后,输出App组件的HTML(所以先渲染组件,再调试是否存在某些文件)
});

test("renders the correct content", () => {
  const { getByText, getByLabelText } = render(<App />);

  getByText("TODOs");
  getByLabelText("What needs to be done?");
  getByText("Add #1");
});

test("renders the correct content by screen", () => {
  render(<App />);

  // expect(screen.getByText(/TODOs~~~~/)).toBeNull(); 不适合用来判断内容是否为空
  expect(screen.queryByText(/TODOs~~~~/)).toBeNull();

  // 用于异步操作,等待有这个文本时
  // await screen.findByText(/Signed in as/);
  
});

4. fireEvent函数来模拟终端用户的交互

fireEvent函数接受一个元素(这里是文本框角色的输入字段)和一个事件(这里是一个值为“JavaScript”的事件)。调试函数的输出应该显示事件前后的HTML结构;您应该可以看到输入字段的新值得到了适当的呈现

// specs/4.simulate.spec.jsx
import { fireEvent, render } from "@testing-library/react";

test("allows users to add items to their list", () => {
  const { getByText, getByLabelText } = render(<App />);

  const input = getByLabelText("What needs to be done?");
  fireEvent.change(input, { target: { value: "RTL Presentation Slides" } });   // 模拟输入内容
  fireEvent.click(getByText("Add #1"));   // 模拟点击

  getByText("RTL Presentation Slides");  // 判断是否有此内容
});
  • fireEvent:只有change事件
  • userEvent用户事件库:建立在fireEvent API上,有change、keyDown、keyPress和keyUp事件(尽可能使用userEvent而不是fireEvent)
import userEvent from '@testing-library/user-event';

test("test userEvent api", () => {
  const { getByText, getByLabelText, getByTestId } = render(<App />);

  const input = getByLabelText("What needs to be done?");
  const addBtn = getByTestId("add-btn");

  userEvent.type(input, "RTL Presentation Slides"); // 输出内容
  userEvent.click(addBtn); // 点击

  getByText("RTL Presentation Slides"); // 判断是否有此内容
});

5. 回调处理程序

使用Jest来模拟外部模块
jest 模拟api

test('calls the onChange callback handler', () => {
     const onChange = jest.fn();  // 创建一个模拟函数
  
     render(
       <Search value="" onChange={onChange}>
         Search:
       </Search>
     );
  
     fireEvent.change(screen.getByRole('textbox'), {
       target: { value: 'JavaScript' },
     });
  
     expect(onChange).toHaveBeenCalledTimes(1);  // 被调用时
});

6. 测试异步代码

import { fireEvent, render, waitFor } from "@testing-library/react";

import { App } from "../src/AsyncApp";

// Normally you can mock the entire module using jest.mock("./api")
import { api } from "../src/api";
const mockCreateItem = (api.createItem = jest.fn()); // 创建一个模拟函数

test("allows users to add items to their list", async () => {
  const todoText = "RTL Presentation Slides";
  mockCreateItem.mockResolvedValueOnce({ id: 123, text: todoText }); // 调用函数并传入参数

  const { getByText, getByLabelText } = render(<App />);

  const input = getByLabelText("What needs to be done?");
  fireEvent.change(input, { target: { value: todoText } });
  fireEvent.click(getByText("Add #1"));

  expect(mockCreateItem).toHaveBeenCalledTimes(1);
  expect(mockCreateItem).toHaveBeenCalledWith(
    "/items",
    expect.objectContaining({ text: todoText })
  );

  await waitFor(() => getByText(todoText));
});

其他使用

搜索类型
  • getByText:<button>Add</button> ,判断是否存在getByText('Add')
  • getByRole:函数通常用于通过aria-label属性检索元素 <button type="button" >
  • getByLabelText :<label for="search" />
  • getByPlaceholderText: <input placeholder="Search" />
  • getByAltText:<img alt="profile" />
  • getByDisplayValue:<input value="JavaScript" />
describe('App', () => {
  test('renders App component', () => {
    render(<App />);
 
    screen.getByRole('');  // 可以使用 expect(screen.getByRole('textbox')).toBeInTheDocument();
  });
});

/*
输出
Unable to find an accessible element with the role ""
 
Here are the accessible roles:
 
document:
 
Name "":
<body />
 
--------------------------------------------------
textbox:
 
Name "Search:":
<input
  id="search"
  type="text"
  value=""
/>
 
--------------------------------------------------
*/
搜索变量

三个搜索变量getBy, queryBy和findBy
它们都返回一个元素数组,并且可以再次与搜索类型(例如:Text, Role, PlaceholderText, DisplayValue)关联
来可以访问的相同搜索类型进行扩展

queryByText
queryByRole
queryByLabelText
queryByPlaceholderText
queryByAltText
queryByDisplayValue

findByText
findByRole
findByLabelText
findByPlaceholderText
findByAltText
findByDisplayValue
findAllByRole
  1. getBy和queryBy的区别?
    getBy: 去断言一个不存在的元素时,进行断言之前会抛出一个错误,因为它找不到包含该文本的元素。为了断言不存在的元素,我们可以用queryBy交换getBy。每次断言某个元素不存在时,请使用queryBy
    在这里插入图片描述
import React from 'react';
import { render, screen } from '@testing-library/react';
 
import App from './App';
 
describe('App', () => {
  test('renders App component', () => {
    render(<App />);
 
    expect(screen.queryByText(/Searches for JavaScript/)).toBeNull();
  });
});
  1. 对于任何尚未存在但最终将存在的元素,使用findBy而不是getBy或queryBy
describe('App', () => {
  test('renders App component', async () => {
    render(<App />);
 
    expect(screen.queryByText(/Signed in as/)).toBeNull();
 
    expect(await screen.findByText(/Signed in as/)).toBeInTheDocument();
  });
});
断言功能
toBeDisabled
toBeEnabled
toBeEmpty
toBeEmptyDOMElement
toBeInTheDocument:和toBeNull检查元素是否存在(和)
toBeInvalid
toBeRequired
toBeValid
toBeVisible
toContainElement
toContainHTML
toHaveAttribute
toHaveClass
toHaveFocus
toHaveFormValues
toHaveStyle
toHaveTextContent
toHaveValue
toHaveDisplayValue
toBeChecked
toBePartiallyChecked
toHaveDescription

参考:
官方文档
How to use React Testing Library Tutorial

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值