react 快速上手开发_React中测试驱动开发的快速指南

news/2024/7/2 23:04:31

react 快速上手开发

by Michał Baranowski

通过MichałBaranowski

React中测试驱动开发的快速指南 (A quick guide to test-driven development in React)

Following the principles of Test-Driven Development (TDD) when writing a front-end React app might seem more difficult than doing the same on the back-end.

编写前端时遵循测试驱动开发 (TDD)的原理 React应用程序似乎比在后端执行相同的操作更加困难。

First, we need to somehow render our component. Then we need to simulate user interaction with a browser. Next we respond to changes in props and state, and finally come up with a way to test asynchronous methods triggered by the click of a button.

首先,我们需要以某种方式呈现我们的组件。 然后,我们需要模拟用户与浏览器的交互。 接下来,我们响应道具状态的变化最后提出一种方法来测试单击按钮触发的异步方法。

Trying to cover all these cases in our tests often results in tests that are difficult to read. They also often depend on one another. We mock a lot, and in return we have tests full of anti-patterns.

试图在我们的测试中涵盖所有这些情况通常会导致难以阅读的测试。 他们还经常相互依赖。 我们进行了很多模拟,作为回报,我们进行了充满反模式的测试。

不要浪费你的时间 (Don’t waste your time)

From what I’ve seen, many programmers create working React components first.Then they try to cover them with tests, just to realize that the components cannot be tested in their current implementation. Then they need to refactor. Because of that they lose patience, time, and their employer’s money.

从我所看到的来看,许多程序员首先创建了可工作的React组件,然后试图通过测试覆盖它们,只是为了意识到无法在当前实现中对组件进行测试。 然后他们需要重构。 因此,他们失去了耐心,时间和雇主的金钱。

可用的解决方案 (Available solutions)

Fortunately for us, there are many testing libraries that can help us address these problems. We can try rendering React components with Enzyme and mock API responses using MockAxios. However, these libraries usually have so many methods and options that it might get confusing, especially for people who have just started writing tests.

对我们来说幸运的是,有许多测试库可以帮助我们解决这些问题。 我们可以尝试使用MockAxios渲染带有酶的 React组件和模拟API响应。 但是,这些库通常具有太多的方法和选项,可能会引起混淆,特别是对于刚开始编写测试的人。

Let’s take Enzyme for example — what’s the difference between the Shallow, Mount and Render methods? And which should you use? This is not what you should be worried about when you write your tests, in my opinion. It should be as straight forward as possible.

让我们以为例-ShallowMountRender方法之间有什么区别? 您应该使用哪个? 我认为这不是编写测试时应该担心的。 它应该尽可能直接。

我们的项目 (Our project)

For our Quick Guide purposes, we’re going to create a small React app. After clicking on a button, a random joke about Chuck Norris will be fetched and displayed.

出于快速指南的目的,我们将创建一个小型React应用。 单击按钮后,将获取并显示有关Chuck Norris的随机笑话。

No one has ever pair-programmed with Chuck Norris and lived to tell about it.
从来没有人与Chuck Norris结对编程并活着讲述这件事。

So let’s begin.

因此,让我们开始吧。

Kick-off by creating a React project in CodeSandbox, and then install the following dependencies (Jest is already pre-installed if you started from the link above):

开球创建一个阵营项目CodeSandbox 然后安装以下依赖性(j EST 如果您从上面的链接开始,则已经预先安装了):

  • axios — used for fetching data from the external API

    axios-用于从外部API提取数据

  • axios-mock-adapter — used for mocking server responses

    axios-mock-adapter-用于模拟服务器响应

  • react-testing-library — light, easy to use testing library for rendering, simulating actions, and handling async methods — created by Kent C. Dodds

    React测试库 —轻巧,易于使用的测试库,用于渲染,模拟动作和处理异步方法—由Kent C. Dodds创建

  • jest — for running the tests and creating assertions

    笑话 —用于运行测试和创建断言

文件夹/文件结构 (Folder/files structure)

  • src/index.js — entry point for our React app

    src / index.js —我们的React应用程序的入口点

  • src/jokeGenerator.js our container component which fetches, controls, and provides data

    src / jokeGenerator.js 我们的容器组件,用于获取,控制和提供数据

  • src/joke.js — simple presentation component

    src / joke.js —简单的演示组件

  • src/__tests__/jokeGenerator.test.js contains our tests

    src / __ tests __ / jokeGenerator.test.js 包含我们的测试

您的第一个测试 (Your first test)

Each time before we create a component we will write a failing test first and then try to make it pass. Let’s start by writing a test for our dummy component <Joke /> which will render a text from props.

每次创建组件之前,我们都会先编写一个失败的测试,然后尝试使其通过 。 让我们首先为虚拟组件<Joke />编写一个测试,该测试将通过props渲染文本。

Reading from the top: we use a render method from the react-testing-library and pass the &lt;Joke/> component (which does not exist at this point) into it. It returns an object containing a few very useful methods (find the full list of available methods here) — for example getByTestId. It then returns an HTML element based on data-testid as an argument.

从顶部开始阅读:我们使用react-testing-library中的render方法,并将& lt; Jok e />组件(目前不存在)传递给它。 它返回一个包含了一些非常有用的方法的对象(可以找到我的完整列表的ThOD这儿) -为前充足getBy TestId。 然后,它返回基于data-t estidHTML元素作为参数。

Next, we write an expect using above method and data-testid, and check if the element contains the text from props. After running the tests, we get:

接下来,我们写一个想到利用上述方法和数据testid,并检查元素都包含道具的文字。 运行测试后,我们得到:

Joke is not defined
笑话未定义

Yep, we want it to fail! <Joke /> does not exist yet, remember? We have only created an empty joke.js file so far. We wrote a test in which we can clearly see what we expect the component to do. Now our job is to make the test pass without modifying the test code. Let’s do that then:

是的,我们希望它失败! <笑话 />还不存在,记得吗? 我们只创建了一个空的乔 ke.j S档为止。 我们编写了一个测试,可以清楚地看到我们期望组件执行的测试。 现在,我们的工作是使测试通过而无需修改测试代码。 然后让我们这样做:

Now, if you did everything just like I did, the test should pass :)

现在,如果您像我一样做所有事情,则测试应该通过:)

第二部分 (Second component)

Our second component will be responsible for fetching a random joke after a user clicks a button. We’ll save it in the component’s state and pass it down to our <Joke /> component. We would also like to display a default message when no joke has been loaded yet.

我们的第二个组件将负责在用户单击按钮后获取一个随机笑话。 我们将其保存为组件状态,并将其传递给我们的<Joke />组件。 当还没有玩笑时,我们还想显示一个默认消息。

Of course, we start with test first. It is a bigger component, so we’ll be writing the test step-by-step. We’ll also make sure it is passing as often as possible.

当然,我们首先要进行测试。 它是一个更大的组件,因此我们将逐步编写测试。 我们还将确保它尽可能多地通过。

We are already familiar with the render method, but this time we are taking getByText from the return object. As you might have guessed, the method returns an HTML Element if one exists in the DOM.

我们已经熟悉了render方法,但是这次我们从返回对象中获取getByText 。 您可能已经猜到了,如果DOM中存在HTML元素,该方法将返回一个HTML元素。

Run the tests and….

运行测试,然后…。

JokeGenerator is not defined
未定义JokeGenerator

You know what to do with it:

您知道如何处理它:

The test is still failing, but this time it outputs a different error:

测试仍然失败,但是这次它输出另一个错误:

Unable to find an element with the text.
找不到带有文本的元素。

You haven’t loaded any jokes yet. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

您还没有玩笑 。 这可能是因为文本被多个元素分解了。 在这种情况下,您可以为文本匹配器提供一个功能,以使匹配器更加灵活。

Let’s quickly fix that by introducing a state to our component and displaying a default message when there is no joke in the state.

让我们快速解决该问题,方法是在组件中引入一个状态并在该状态中没有笑话时显示默认消息。

Tests are passing now, so we can move on to add new functionality. Imagine that when we click on a button, the default text in the component disappears to make room for a “Loading…” message. Sounds pretty straightforward, right? We can test this scenario with only three lines of code!

测试现已通过,因此我们可以继续添加新功能。 想象一下,当我们单击一个按钮时,组件中的默认文本消失了,从而为“ 正在加载... ”消息腾出了空间。 听起来很简单,对吧? 我们可以只用三行代码测试此业务情景!

Let’s import the Simulate method first, as we’re going to need that:

首先,我们需要导入Simulate方法,因为我们需要这样做:

import { render, Simulate } from “react-testing-library”
从“ react-testing-library”导入{render,Simulate}

The difference between queryByText and getByText is in what each one returns when the element is not found. The first one returns null and the second one throws an error message. Re-running the tests:

queryByTextgetByText之间的区别在于,当找不到该元素时,每个返回的内容。 第一个返回null ,第二个返回错误消息 。 重新运行测试:

Unable to find an element with the text: Load a random joke

找不到带有文本的元素: 加载一个随机笑话

We need to create a button and set the onClick method which will set the loading state to true.

我们需要创建一个按钮并设置onClick方法,该方法会将加载状态设置为true

Just like that the test is passing again. Now it’s time to fetch our random joke! Well… it won’t be random in our tests. We’ll mock it using MockAxios.

就像测试再次通过一样。 现在是时候获取我们的随机笑话了! 好吧……在我们的测试中不会是随机的。 我们将使用MockAxios对其进行模拟

import * as axios from "axios"

从“ axios”将*作为axios导入

Above our tests in jokeGenerator.test.js, insert these two lines of code:

jokeGenerator.test.js中的测试上方,插入以下两行代码:

The first line creates a new instance of MockAxios with a random delay. The second line takes and executes a callback function after running all the tests in this file, and removes the mocked state from axios.

第一行创建一个随机延迟的MockAxios新实例。 第二行在运行此文件中的所有测试之后,采用并执行回调函数,并从axios中删除模拟状态。

At the top of our second test where we test the <JokeGenerator /> component, add:

在我们测试<JokeGenerator />组件的第二个测试的顶部,添加:

It mocks the response of any GET call done via axios. At the end of the same test:

模拟了通过axios完成的任何GET调用的响应。 在同一测试结束时:

Don’t forget to import wait:

不要忘记导入wait

import { render, Simulate, wait } from “react-testing-library”
从“ react-testing-library”导入{渲染,模拟,等待}

The wait method waits (4500ms by default) until a callback function stops throwing an error. It is checked at 50ms intervals. Basically we’re just waiting until the loading message disappears from the DOM.

wait方法等待(默认为4500ms),直到回调函数停止引发错误为止。 每隔50毫秒检查一次。 基本上,我们只是在等待加载消息从DOM中消失。

wait is also available as a separate npm package (react-testing-library uses it as a dependency). It was created by Łukasz Gozda Gandecki.

wait也可以作为单独的npm包提供 ( react-testing-library使用它作为依赖项)。 它是由ŁukaszGozda Gandecki创建的。

After making all of the code modifications and running the tests, we should get the following fail message:

在完成所有代码修改并运行测试之后,我们应该获得以下失败消息:

Expected the element not to be present

预期该元素存在

What do you think it might be? According to our test, we expect the loading message to be gone. Additionally, we want to fetch our joke from the API and save it to the state so that next expect passes.

您认为可能是什么? 根据我们的测试,我们希望加载消息消失。 另外,我们想从API中获取笑话并将其保存到状态,以便下次期望通过。

Tests should pass again now. We are sure that everything works as expected…aren’t we? Notice that we have never opened our browser and verified manually if our app even works…However, thanks to how we were writing our tests (so that our tests resemble the way the user would use the application), we can be almost 100% sure that our small app is simply working.

现在应该再次通过测试。 我们确信一切都会按预期进行,对吗? 请注意,我们从未打开过浏览器并手动验证过我们的应用程序是否可以正常工作 ……但是,由于我们编写测试的方式( 使我们的测试类似于用户使用应用程序的方式 ),我们几乎可以百分百确定我们的小型应用程序只是在工作。

As the last piece of code, let’s add this to the index.js and open the browser :)

作为最后的代码,让我们将其添加到index.js并打开浏览器:)

奖金 (Bonus)

Because of the way we wrote our tests, we can utilize them as e2e tests without adding a single line of code! All we need to do is to remove all the lines related to MockAxios and run the tests again! They will now use a real external API. Cool, isn’t it? :)

由于我们编写测试的方式,我们可以将它们用作e2e测试, 而无需添加任何代码! 我们要做的就是删除所有与MockAxios相关的行, 然后再次运行测试! 他们现在将使用真正的外部API。 不错,不是吗? :)

摘要 (Summary)

All the code is available on the project’s CodeSandbox. I really encourage you to get familiar with a full react-testing-library documentation. You’ll find there many more examples and use cases.

所有代码都可以在项目的CodeSandbox上找到 我真的鼓励您熟悉完整的react-testing-library文档。 您会发现更多示例和用例。

I hope you enjoyed my Quick Guide to TDD in React, and that you’ve learned something new today.

希望您喜欢React中的TDD快速指南,并且今天学到了新知识。

翻译自: https://www.freecodecamp.org/news/quick-guide-to-tdd-in-react-81888be67c64/

react 快速上手开发


http://lihuaxi.xjx100.cn/news/239484.html

相关文章

前端部分面试题整理,欢迎补充

1.ng中如何配置路由&#xff0c;$scope和$rootscope的原理ng中如何配置路由?1)使用内置路由模块ng-routevar app angular.module(ngRouteExample, [ngRoute]).controller(MainController, function($scope) {}).config(function($routeProvider, $locationProvider) {$routeP…

远程协助软件开发_这是我从事远程软件开发人员工作的主要技巧

远程协助软件开发by Colin Morgan通过科林摩根(Colin Morgan) 这是我从事远程软件开发人员工作的主要技巧 (Here are the top tips I’ve used to land a remote software developer job) Applying for a remote software developer job means you are voluntarily choosing t…

linux查找文件

find . | grep xyz 将当前目录下&#xff08;包括子目录&#xff09;的文件名中含有xyz的文件过滤出来 find . | xargs grep xyz 将当前目录下&#xff08;包括子目录&#xff09;的文件内容中含有xyz的行过滤出来 转载于:https://www.cnblogs.com/anovana/p/8036032.html

mysql查询解析过程_MySQL查询执行过程详解

查询是用户通过设置某些查询条件&#xff0c;从表或其他查询中选取全部或者部分数据&#xff0c;以表的形式显示数据供用户浏览。查询是一个独立的、功能强大的、具有计算功能和条件检索功能的数据库对象。MySQL数据库中&#xff0c;MySQL查询同样是数据库的核心操作&#xff0…

Java云托管服务的开支削减策略

\摘要\随着项目不断扩大&#xff0c;你需要将其迁移到更大的虚拟机上。但如果新虚拟机环境超出了你的需求则会产生额外开支。\相比虚拟机&#xff0c;容器具有更小的粒度&#xff0c;并且无需重启运行中的实例即可垂直扩展。\单体应用和历史遗留应用无需更改配置&#xff0c;即…

flask url构建_如何为生产构建构建Flask-RESTPlus Web服务

flask url构建by Greg Obinna由格雷格奥比纳(Greg Obinna) 如何为生产构建构建Flask-RESTPlus Web服务 (How to structure a Flask-RESTPlus web service for production builds) In this guide I’ll show you a step by step approach for structuring a Flask RESTPlus web…

第一个spring boot应用

前提 首先要确保已经安装了java和maven&#xff1a; $ java -version java version "1.8.0_102" Java(TM) SE Runtime Environment (build 1.8.0_102-b14) Java HotSpot(TM) 64-Bit Server VM (build 25.102-b14, mixed mode) $ mvn -v Apache Maven 3.3.9 (bb52d850…

刷新mysql的权限相关表_flush privileges刷新MySQL的系统权限相关表

mysql> flush privilegesmysql> update mysql.user set passwordPASSWORD(‘新密码’) where User’root’;mysql> flush privileges;mysql> quit答:mysql 新设置用户或更改密码后需用flush privileges刷新MySQL的系统权限相关表&#xff0c;否则会出现拒绝访问&am…