使用React Native Testing库进行组件测试(案例详解)
原文:https://around25.com/blog/component-testing-using-react-native-testing-library/
译者:明月
主要思想是弄清楚该软件的每个组件是否按照预期工作。仅当软件的每个单元正常工作时,环境才能有效地工作并按预期运行。
作为开发人员,您可以进行单元测试,也可以由独立的测试人员执行。
测试初学者的注意事项:单元测试可以是手动的也可以是自动的。整个过程涉及创建测试用例,对这些用例进行审查,重新处理然后执行。
单元测试作为功能实现。通过引用各种输入值来检查功能的输出。这使调试过程变得更加容易。
整个软件开发过程变得敏捷:引入任何新的修改后,就可以单独测试组件并将其集成到系统中,从而节省大量时间和成本。
由于可以在早期阶段检测和管理漏洞,因此减少了最后获得烦人bug的机会。整体重构和更新变得更加方便。
由于您已经进行了单元测试,可以为每个模块及其功能提供深入的概述,因此文档将变得不再那么繁琐。
整个代码成为模块化的。这将是代码变得可靠和可重用。
它有助于从统计角度衡量每个组件和整个系统的性能,同时还能确定组件在系统中覆盖的区域。
您避免交付容易出错的产品。另外,您可以在降低复杂性的同时提高代码质量。
总体而言,单元测试是交付成功产品的最安全方法之一。
React Native测试库 提供了简单而完整的 React Native测试实现程序。
https://github.com/callstack/react-native-testing-library
这是用于测试React Native组件的轻量级解决方案,它在react-test-renderer之上促进了实用方法。这将鼓励更好的测试实践。该库与Jest并驾齐驱,但也可以与其他测试运行者合作。
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-renderer的peerDependencies列表。
对于测试人员,我们将在这里使用 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();
});
"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,我们下篇见~
2021-05-28
2021-05-26
在看点这里