React 测试库入门

Avatar of Kingsley Silas
Kingsley Silas

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费信用额度!

我可以猜到你在想什么:另一个 React 测试库?CSS-Tricks 上已经介绍过很多了(哎呀,我之前已经发布过一篇 介绍 Jest 和 Enzyme 的文章),难道选项还不够多吗?

但是 react-testing-library 不仅仅是另一个测试库。它是一个测试库,没错,但它建立在一个将它与其他库区分开来的基本原则之上。

您的测试越类似于软件的使用方式,它们就能为您提供越大的信心。

它试图解决用户如何使用您的应用程序的测试。事实上,它的设计方式使得即使您重构组件,测试也不会中断。我知道我们在 React 之旅的某个时刻都遇到过这种情况。

我们将花一些时间一起使用 react-testing-library 为我构建的一个简单的待办事项应用程序编写测试。您可以克隆 该仓库 到本地。

git clone https://github.com/kinsomicrote/todoapp-test.git

如果您这样做了,接下来安装所需的软件包。

## yarn
yarn add --dev react-testing-library jest-dom

## npm
npm install --save-dev react-testing-library jest-dom

如果您想知道为什么 Jest 在那里,我们正在使用它进行断言。在 src 目录中创建一个名为 __test__ 的文件夹,并创建一个名为 App.test.js 的新文件。

获取快照

快照测试会记录对已测试组件执行的测试,以便以可视化的方式查看更改之间发生了哪些变化。

当我们第一次运行此测试时,我们会获取组件外观的第一个快照。因此,第一个测试注定会通过,因为,嗯,没有其他快照可以与之比较,这会表明某些内容失败了。只有当我们通过添加新元素、类、组件或文本来对组件进行新的更改时,它才会失败。添加在创建快照或上次更新时不存在的内容。

快照测试将是我们在这里编写的第一个测试。让我们打开 App.test.js 文件并使其如下所示

import React from 'react';
import { render, cleanup } from "react-testing-library";
import "jest-dom/extend-expect";
import App from './App';

afterEach(cleanup);

it("matches snapshot", () => {
  const { asFragment } = render(<App />);
  expect(asFragment()).toMatchSnapshot();
});

这将导入我们用于编写和运行测试的必要软件包。render 用于显示我们要测试的组件。我们使用 cleanup 在每次测试运行后清除内容——正如您在 afterEach(cleanup) 行中看到的那样。

使用 asFragment,我们获取渲染组件的 DocumentFragment。然后我们期望它与已创建的快照匹配。

让我们运行测试看看会发生什么。

## yarn
yarn test

## npm
npm test

正如我们现在所知,如果这是我们的第一个测试,组件的快照将在 __tests__ 目录中名为 __snapshots__ 的新文件夹中创建。我们实际上会在其中获得一个名为 App.test.js.snap 的文件,它看起来像这样

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`matches snapshot 1`] = `
<DocumentFragment>
  <div
    class="container"
  >
    <div
      class="row"
    >
      <div
        class="col-md-6"
      >
        <h2>
          Add Todo
        </h2>
      </div>
    </div>
    <form>
      <div
        class="row"
      >
        <div
          class="col-md-6"
        >
          <input
            class="form-control"
            data-testid="todo-input"
            placeholder="Enter a task"
            type="text"
            value=""
          />
        </div>
      </div>
      <div
        class="row"
      >
        <div
          class="col-md-6"
        >
          <button
            class="btn btn-primary"
            data-testid="add-task"
            type="submit"
          >
            Add Task
          </button>
        </div>
      </div>
    </form>
    <div
      class="row todo-list"
    >
      <div
        class="col-md-6"
      >
        <h3>
          Lists
        </h3>
        <ul
          data-testid="todos-ul"
        >
          <li>
            <div>
              Buy Milk
              <button
                class="btn btn-danger"
              >
                X
              </button>
            </div>
          </li>
          <li>
            <div>
              Write tutorial
              <button
                class="btn btn-danger"
              >
                X
              </button>
            </div>
          </li>
        </ul>
      </div>
    </div>
  </div>
</DocumentFragment>
`;

现在,让我们测试 DOM 元素和事件

我们的应用程序包含两个默认情况下在应用程序首次运行时显示的待办事项。我们要确保它们实际上在第一次应用程序运行时显示,因此,要测试这一点,我们必须定位无序列表 (<ul>) 并检查长度。我们期望长度等于二——项目的数量。

it('it displays default todo items', () => {
  const { getByTestId } = render(<App />);
  const todoList = getByTestId('todos-ul');
  expect(todoList.children.length).toBe(2);  
});

我们在该代码段中使用 getByTestIdApp 组件中提取测试 ID。然后我们设置 todoList 以定位 todos-ul 元素。这应该是返回为二的结果。

使用我们到目前为止学到的知识,看看您是否可以编写一个测试来断言用户可以在输入字段中输入值。以下是您需要执行的操作

  • 获取输入字段
  • 为输入字段设置一个值
  • 触发更改事件
  • 断言输入字段的值与您在步骤 2 中为其设置的值相同

不要偷看下面的答案!花尽可能多的时间。

还在继续吗?太好了!我去喝杯咖啡,马上回来。

嗯,咖啡。☕️

哦,你完成了!你真棒。让我们比较一下答案。我的看起来像这样

it('allows input', () => {
  const {getByTestId } = render(<App />)
  let item = 'Learn React'
  const todoInputElement = getByTestId('todo-input');
  todoInputElement.value = item;
  fireEvent.change(todoInputElement);
  expect(todoInputElement.value).toBe('Learn React')
});

使用 getByTestId,我能够提取应用程序中的测试 ID。然后我创建一个变量,将其设置为字符串 Learn React,并将其作为输入字段的值。接下来,我使用其测试 ID 获取输入字段,并在设置输入字段的值后触发更改事件。完成此操作后,我断言输入字段的值确实是 Learn React

这与您的答案一致吗?如果您有其他方法,请留下评论!

接下来,让我们测试一下我们是否可以添加新的待办事项。我们需要获取输入字段、添加新项目的按钮和无序列表,因为这些是创建新项目所需的所有元素。

我们为输入字段设置一个值,然后触发按钮点击以添加任务。我们能够通过使用 getByText 获取按钮来做到这一点——通过在文本为 Add Task 的 DOM 元素上触发点击事件,我们应该能够添加一个新的待办事项。

让我们断言无序列表元素中的子元素(列表项)数量等于三。这假设默认任务仍然完好无损。

it('adds a new todo item', () => {
  const { getByText, getByTestId } = render(<App />);
  const todoInputElement = getByTestId('todo-input');
  const todoList = getByTestId('todos-ul');
  todoInputElement.value = 'Learn React';
  fireEvent.change(todoInputElement);
  fireEvent.click(getByText('Add Task'))
  expect(todoList.children.length).toBe(3); 
});

非常棒,对吧?

这只是在 React 中进行测试的一种方法

您可以在下一个 React 应用程序中尝试 react-testing-library。 仓库中的文档 非常详尽,并且——与大多数工具一样——是最佳的起点。 Kent C. Dodds 创建了它,并在 Frontend Masters 上开设了关于测试的完整课程(需要订阅),该课程还涵盖了 react-testing-library 的来龙去脉。

也就是说,这只是 React 的一个测试资源。当然还有其他资源,但希望这是您现在感兴趣尝试的一个资源,因为您已经了解了一些内容,但当然,请使用最适合您项目的资源。