使用React Native Testing库进行组件测试(案例详解)

前端UpUp

共 7970字,需浏览 16分钟

 · 2021-06-01

原文:https://around25.com/blog/component-testing-using-react-native-testing-library/

译者:明月


在本篇文章中,我们将探讨 React Native 应用程序开发生态系统中的单元测试基础知识。首先,让我们了解一下什么是单元测试以及为什么在任何软件开发生命周期中都需要用到它。

简介:单元测试


在单元测试中,项目中的每个组件都作为一个单元进行单独测试。这样就可以测试独立的组件,直到它们满足设计和功能规范要求。

主要思想是弄清楚该软件的每个组件是否按照预期工作。仅当软件的每个单元正常工作时,环境才能有效地工作并按预期运行。

作为开发人员,您可以进行单元测试,也可以由独立的测试人员执行。

测试初学者的注意事项:单元测试可以是手动的也可以是自动的。整个过程涉及创建测试用例,对这些用例进行审查,重新处理然后执行。


为什么单元测试必不可少


简而言之,它是关于采用测试驱动的开发方法。测试驱动的开发涉及在将组件的每个模块引入主系统之前对其进行开发和测试,这是构建系统的一种更为精简的方法。

通过使用单元测试,您可以获得:
  • 单元测试作为功能实现。通过引用各种输入值来检查功能的输出。这使调试过程变得更加容易。

  • 整个软件开发过程变得敏捷:引入任何新的修改后,就可以单独测试组件并将其集成到系统中,从而节省大量时间和成本。

  • 由于可以在早期阶段检测和管理漏洞,因此减少了最后获得烦人bug的机会。整体重构和更新变得更加方便

  • 由于您已经进行了单元测试,可以为每个模块及其功能提供深入的概述,因此文档将变得不再那么繁琐

  • 整个代码成为模块化的。这将是代码变得可靠和可重用。

  • 它有助于从统计角度衡量每个组件和整个系统的性能,同时还能确定组件在系统中覆盖的区域。

  • 您避免交付容易出错的产品。另外,您可以在降低复杂性的同时提高代码质量

总体而言,单元测试是交付成功产品的最安全方法之一。


React Native中的单元测试入门


React Native测试库 提供了简单而完整的 React Native测试实现程序。

https://github.com/callstack/react-native-testing-library

这是用于测试React Native组件的轻量级解决方案,它在react-test-renderer之上促进了实用方法。这将鼓励更好的测试实践。该库与Jest并驾齐驱,但也可以与其他测试运行者合作。


入门项目


为为了在React Native 项目中进行测试,我们首先需要一个入门项目,您可以继续从snack项目中下载并启动项目。
https://snack.expo.io/59ZFgP9Dn

下载后,我们需要通过执行以下命令在仿真器或连接的设备上运行项目:

Expo start

在应用程序中,我们有一个输入字段,一个用于添加项目的按钮和一个列表。该列表包含一个删除按钮,用于删除列表项。主页分为三个部分:

  • 主主屏幕(Home.js)

  • 列表组件(ItemsList.js)

  • 添加项目组件(ItemAdder.js)

要执行单元测试,我们需要将React Native测试库安装到我们的项目中。为此,我们需要在项目终端中运行以下命令:

npm install --save-dev @testing-library/react-native


该库有一个react-test-rendererpeerDependencies列表。

对于测试人员,我们将在这里使用 jest。因此,我们需要安装jest-native库作为附加的jest匹配器。为此,我们需要在项目终端中运行以下命令:

npm install --save-dev @testing-library/jest-native


现在,我们需要配置我们的jest设置文件,即package.json文件中的模式。为此,我们在package.json文件中存在的jest对象中添加以下配置行:

{  "jest": {    "preset": "react-native",    "setupFilesAfterEnv": ["@testing-library/jest-native/extend-expect"],    "transformIgnorePatterns": ["node_modules/(?!(jest-)?react-native|@?react-navigation)"],    "setupFiles": ["./node_modules/react-native-gesture-handler/jestSetup.js"]  }}

我们已完成配置,但未执行测试。


因此,我们在一个单独的文件夹中创建一个新的测试文件,我们将在项目的./screens目录中调用./test。在其中,我们创建一个名为Home.test.js的文件,该文件将包含与主屏幕有关的所有测试,并在运行测试命令后执行。


现在是时候将所需的库导入Home.test.js文件中以进行测试了:

import React from 'react';import {render, fireEvent} from '@testing-library/react-native';import Home from '../Home';
测试主屏幕的渲染


为了测试基本的主屏幕模板,我们将主屏幕模板代码渲染到终端中。


如果一切正确呈现,那就太好了!没有问题。现在使用render()方法并将Home组件传递给它。

渲染方法将为我们提供测试主屏幕内部组件所需的方法。

同样,我们在这里使用debug方法来呈现主屏幕模板:

test('rendering Home component', async () => {    const {debug} = render(<Home/>);      debug();});


此时,我们可以使用两个参数来实现测试功能,第一个参数是测试的名称,第二个参数是在执行测试时触发的回调函数。
在回调函数中,我们可以测试任何组件中的每个元素。
在package.json文件中,我们有一个脚本来运行测试:
"scripts": {        "android": "react-native run-android",        "ios": "react-native run-ios",        "start": "react-native start",        "test": "jest",        "lint": "eslint ."}

每次我们要执行测试时,都需要在项目终端中运行以下命令:

npm run test -watch


因此,我们将获得主屏幕模板的完整模板渲染:

如果发生任何错误,则模板JSX元素的集成可能存在问题,该问题将在错误消息本身中指示。

测试输入字段和按钮组件的工作情况


主屏幕中的输入字段和按钮组件是从ItemAdder组件导入的。

要测试TextInput和Button,我们需要为其分配testID属性。使用这些testID道具,我们将能够在测试配置中掌握这些元素。

<TextInput  value={input}  onChangeText={setInput}  placeholder="Add Item..."  style={styles.input}  testID={`${testID}-input`}/><Button  color="#000"  testID = "input-button"  title="+ ADD"  style = {styles.button}  onPress={() => {    addItem(input);    setInput('');  }}/>

现在,我们可以使用Home.test.js文件中的getByTestId方法访问这些输入和按钮。然后我们通过提及它们的testID来初始化输入字段和按钮:

test('testing input and button', async () => {    const {getByTestId, getByText} = render(         <Home />,    );  
const input = getByTestId('adder-input'); const button = getByTestId('input-button');
});

接下来,我们使用从测试库导入的fireEvent对象实例,应用输入并触发按钮单击。我们可以在输入中输入文本值,并以此触发按钮的按下:

fireEvent.changeText(input, 'element');fireEvent.press(button);

在这里,我们使用fireEvent类中的changeText方法将输入提供给我们应用程序中的TextInput元素。我们提供的输入值为'element'。按下功能将触发我们应用中的“添加”按钮,这将使输入显示在列表中。

问题是:如何触发触发事件后,输入值“ element”是否已出现在列表中?

我们可以检查渲染中是否存在文本值“ element”。

为此,我们可以利用render()提供的getByText方法。如果“元素”在渲染器中可用,则将其分配给元素常量。该代码在下面的代码片段中提供:

const element = getByText('element');

现在,我们需要检查元素是否存在于渲染器中。在其他情况下,我们期望element值出现在render中。

这使我们可以使用Expect函数中的tobeDefined方法,该方法将元素作为参数。如果存在该值,则不会发生错误。否则,将发生错误。

expect(element).toBeDefined();

检查以下完整的测试功能:

test('testing input and button', async () => {    const {getByText, getByTestId} = render(        <Home />,    );  
const input = getByTestId('adder-input'); const button = getByTestId('input-button'); fireEvent.changeText(input, 'element'); f ireEvent.press(button); const element = getByText('element'); expect(element).toBeDefined();});


在这里,我们希望在添加输入时定义元素并获得此测试结果:

我们可以看到测试已经通过。


这就是为什么我们现在也可以检查多个输入的原因。为此,我们分配了一个不同的输入值'element1',并像之前的测试一样进行了所有操作。函数是这样的:

test('testing input and button', async () => {    
const {getByText, getByTestId} = render( <Home />, );
const input = getByTestId('adder-input'); const button = getByTestId('input-button'); fireEvent.changeText(input, 'element'); fireEvent.press(button); const element = getByText('element'); expect(element).toBeDefined();
fireEvent.changeText(input, 'element1'); fireEvent.press(button); const element1 = getByText('element1'); expect(element1).toBeDefined();});

如果运行测试命令,则会得到以下结果:


测试删除按钮


在应用程序演示中,您会注意到,当我们将项目添加到列表中时,我们还会获得一个删除按钮。

当我们按下它时,该项目消失。要测试按钮,我们需要将testID属性分配给ItemList.js文件中的delete Button组件:

<Button    title="Delete"    onPress={() => deleteItem(value)}    color="#f12"    testID="delete-list-item"/>


现在在Home.test.js中,我们需要创建一个称为“ testing delete”的新测试函数。

要测试删除按钮,首先我们必须在列表中有一个项目。

因此,我们将使用与以前相同的编码实现来输入项目,并检查其渲染。然后,我们使用getAllByTestID方法,该方法将每个元素以及在其中定义的特定testID定义为数组。

现在,将数组分配给deleteButton常量:我们像以前一样使用fireEvent类中的press方法触发按钮。但是,由于我们仅删除列表的第一项,因此我们还需要分配数组标识符。这将导致列表中的第一项被删除。

因此,我们使用queryByText检查该第一项在渲染中是否可用。然后,通过应用toBeNull从方法期望的功能,我们可以检查数组的第一个项目是否可用或者不渲染。在这里获取功能:

test('testing delete', async () => {    
const {getByText, getByTestId, getAllByTestId, queryByText} = render( <Home />, );
const input = getByTestId('adder-input'); const button = getByTestId('input-button'); fireEvent.changeText(input, 'element'); fireEvent.press(button); const element = getByText('element'); expect(element).toBeDefined();
const deleteButton = getAllByTestId('delete-list-item'); fireEvent.press(deleteButton[0]); expect(queryByText('element')).toBeNull(); });


我们在这里所做的是,我们使用tobeNull方法将'element'值添加到列表中,然后将其删除并检查是否删除了'element' 。

如果答案是正确的,则该元素将被删除,并且删除功能将起作用。测试也应该成功。现在,如果运行测试,我们将得到以下结果:

如我们所见,我们对删除按钮的测试也成功。


测试输入后是否存在项目列表


现在,对于下一个测试,我们在输入值并单击“添加”按钮后检查列表项是否可用。

test('testing list items', async () => {    
const {getByText, getByTestId} = render( <Home />, );
const input = getByTestId('adder-input'); const button = getByTestId('input-button');
fireEvent.changeText(input, 'element'); fireEvent.press(button); const element = getByText('element'); expect(element).toBeDefined();
fireEvent.changeText(input, 'element1'); fireEvent.press(button); const element1 = getByText('element1'); expect(element1).toBeDefined();
const listElements = getByTestId("list"); expect(listElements).toContainElement(element);});

所有步骤,不同的类似,我们使用toContainElement从期望函数来检查我们在列表中输入特定的产品存在。因此,我们应该得到以下结果:

通过准备不同的测试功能并分配测试ID来渲染元素,我们可以测试其他组件。



结论


哇,很长的一段路程。让我们快速回顾一下我们学到的东西:

  • 什么是单元测试

  • 为什么它是必不可少的

  • 如何使用React Native Testing库在React Native生态系统中执行它。

最重要的一点是:通过基于组件渲染编写这些类型的测试,我们可以验证React Native应用程序的各个组件。如果发生任何错误,这将有助于优化应用程序。

测试库提供了其他API和方法,您可以自由探索。面临的挑战是为各个组件设计自己的测试。您甚至可以先创建一个组件,然后按上述步骤对其进行测试,然后再将其集成到核心应用中。这称为测试驱动开发。我们仅用几行代码即可执行测试的简单性令人惊叹。


最后希望您能够使用 React Native Testing 库在React Native中掌握基于组件的基本测试方法。


以上对单元测试的原理进行了详细的阐述,并通过 React Native Testing 库结合具体的案例进行了详细的代码输出 ,如果您认为对您有帮助,请记得 点赞留言分享收藏 哦~~


最后

面试交流群持续开放,分享了近 许多 个面经。
加我微信: DayDay2021,备注面试,拉你进群。

我是 TianTian,我们下篇见~

如何使用GPU改善JavaScript性能

2021-05-28

编译的速度与激情:从10mins到1s

2021-05-26

使用React 360创建虚拟现实体验

2021-05-24


在看点这里
浏览 134
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报