使用React进行TDD的低摩擦方式

2020年12月30日11:02:48 发表评论 52 次浏览

本文概述

首先编写测试。

  • 碧昂斯(意译)

如果你像Beyoncé, 就知道只有在经过一系列可靠的测试后才能编写UI组件代码。

理想地。你知道, 如果那样解决。你尽力了。

事实是, 测试驱动开发(TDD)正统建议你可能不应该编写依赖特定实施策略的测试。但是对于React开发人员来说, 流行的工具(例如Enzyme和React Test Renderer)使编写与源代码的细节紧密结合的测试变得如此容易。

不要误会我的意思:它们是很棒的工具, 但是它们的API使得编写单元测试几乎不可能, 如果组件名称或DOM选择器发生更改, 单元测试也不会中断。

什么是好的测试?

你不会发现缺少任何关于良好的单元测试实践的想法或书籍。他们说, 如果你想编写一个好的测试, 请以公共接口为目标。因此, 如果是一个类, 请测试该类的公共方法, 而不是私有方法。在测试传统的面向对象程序时, 这种传统的观念对我们很有帮助。

不过, React应用程序不是传统的面向对象程序。它们的公共接口不应被视为对象之间的消息。相反, 应将React组件的公共接口视为用户可以看到或与之交互的部分。

最终, 这就是UI组件库的重点:它管理用户和应用程序之间的消息。因此, 如果你想为自己的React代码编写良好的单元测试, 请以公共界面为目标-用户可以感知并与之交互的部分。

用户可以看到什么文字?它们与元素交互时会发生什么变化?

这些是组件单元测试应回答的问题。碰巧的是, 这些类型的问题与产品涉众倾向于为用户界面编写的需求类型非常相似。

考虑一个简单的天气应用程序的以下规范:

我们为制作了一个自定义演示.
不完全是。点击这里查看.

使用React进行TDD的低摩擦方式1
  • 该页面上应该有一个带有文本" Get weather"的按钮
  • 当用户点击按钮时, 纽约的当前天气应出现

就像一个优秀的TDD一样, 你开始针对每一个标准编写测试。让我们来看看一种使用流行的方法酵素图书馆:

import { mount } from "enzyme";

describe("App", () => {
  it("should render a button", () => {
    const element = mount(<App />);
    const button = element.find("button");
    expect(button).toHaveLength(1);
  });

  it("should fetch a weather forecast upon tapping the button", async () => {
    const element = mount(<App />);
    const button = element.find("button");
    button.simulate("click");
    await wait(2000); // homespun utility function that waits X milliseconds before resolving
    expect(element.text()).toContain("New York");
    expect(element.text()).toContain("°");
  });
});

这是一项非常有用的测试, 但它很脆弱。此测试取决于<按钮>元素是互动的。

我们都知道, 当产品要求说"按钮"时, 它们的意思是"看起来和作用类似于按钮的事物"。

那么, 如果在实施过程中我们决定最好使用<div tabindex =" 0" role =" button">or<a href="/forecast-for-new-york">?

如果请求时间超过两秒钟怎么办?我们当然需要更新测试。那不是世界的尽头, 但这也不是TDD理想地完成的方式。

此功能非常简单, 以至于这些边缘情况肯定属于" nitpick"类别, 但是不难想象这些区别在哪里变得更加重要。的纽扣选择器是一个"专用"界面, 因此用户既不会察觉也不在乎使用哪个HTML标签来表示元素。

他们只想找到"获取天气"并获取当下天气!如果只花费100毫秒, 他们也不会等待两秒钟。

请记住:组件的真正公共接口是用户能够感知并与之交互的接口, 而不是选择器或组件名称之类的抽象。

因此, 如果酶和类似工具React测试渲染器不会成功的, 那会怎样?

反应测试库

反应测试库, 当然!最初于2018年初发布, 这位相对较新的人有充分的理由在React社区中积聚了大量精力。

React Testing Library的API提供了一种以用户为中心的替代酶(Enzyme)等的替代方法。让我们编写与之前相同的两个测试, 这次使用React Testing Library(RTL):

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

describe("App", () => {
  it("should render a button", () => {
    render(<App />);
    const button = screen.queryByText("Get weather");
    expect(button).not.toBeNull();
  });

  it("should fetch a weather forecast upon tapping the button", async () => {
    render(<App />);
    const button = screen.queryByText("Get weather");
    fireEvent.click(button);
    await screen.findByText("New York", { exact: false });
    expect(screen.queryByText("°", { exact: false })).not.toBeNull();
  });
});

RTL的API很简单。我们通过标签, 文本, aria标签或其他面向用户的属性来查找元素。选择器完全被淘汰。

与find(" DestinysChildImg")和在findByAltText("命运的孩子在2022年的团圆之旅中表演").

我还想引起大家注意RTL为我们提供的几种处理异步功能的工具之一:

await screen.findByText("New York", { exact: false });

这确实符合你的想法。

findByText间隔查询DOM, 直到(a)超时或(b)找到元素。在现实生活中, 异步服务的响应时间会大大不同, 具体取决于该服务的性能以及客户端设备的功率和带宽。

但是, JavaScript单元测试通常会通过模拟或最佳猜测等待功能竭尽全力来驯服异步效果。 RTL的异步工具解决了在测试套件中表达异步效果的问题。换句话说, 它就像你的用户一样等待页面更改。

这与2020年的JavaScript测试套件所能达到的普通英语水平非常接近。使用RTL将产品需求转换为单元测试非常直观, 这使其成为TDD从业人员的理想选择。

因此, 我们已经编写了测试。让我们结束循环并编写一些源代码!

首先, 我们将进行第一个测试通过:

import React from "react";

const App = () => {
  return (
    <div>
      Get weather
    </div>
  );
};

export default App;

然后, 我们将通过介绍互动元素来进行第二次测试通过:

import React, { useState } from "react";

const App = () => {
  const [weather, updateWeather] = useState(null);

  const fetchAndUpdateWeather = async () => {
    const response = await fetch("https://wttr.in/New York?format=3");
    const body = await response.text();
    updateWeather(body);
  };

  return (
    <div>
      <div>{weather}</div>
      <div>
        <button onClick={fetchAndUpdateWeather}>Get weather</button>
      </div>
    </div>
  );
};
export default App;

我不在乎应用有多小, 我永远不会厌倦第一次看到测试变成绿色。极乐。

我已将测试和源代码汇总到CodeSandbox中, 以供你参考:

总结

在React Testing Library中, 你将获得一个支持真正的测试驱动开发的工具:该API几乎迫使你通过最终用户可以感知的项目进行查询, 并且在逼近现实世界用户行为的同时优雅地处理了异步效果。

你最终可以首先编写React单元测试, 而无需担心源代码的外观。

"不可替代的"

  • 碧昂斯, 但这次真的

全面了解生产React应用

调试React应用程序可能很困难, 尤其是当用户遇到难以重现的问题时。如果你有兴趣监视和跟踪Redux状态, 自动显示JavaScript错误以及跟踪缓慢的网络请求和组件加载时间,

尝试notlogy

.

使用React进行TDD的低摩擦方式2
LogRocket仪表板免费试用横幅

日志火箭就像Web应用程序的DVR, 实际上记录了React应用程序中发生的一切。你可以汇总并报告问题发生时应用程序所处的状态, 而不用猜测为什么会发生问题。 notlogy还监视你的应用程序的性能, 并使用客户端CPU负载, 客户端内存使用情况等指标进行报告。

notlogy Redux中间件软件包在你的用户会话中增加了一层可见性。 notlogy记录Redux存储中的所有操作和状态。

现代化如何调试React应用程序-免费开始监控.

一盏木

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: