[ReactNative翻译]对应用程序的关键部分进行测试

104 阅读11分钟

本文由 简悦 SimpRead转码, 原文地址 www.callstack.com

检查新应用程序是否一切正常至关重要。关注关键部分并运行 te......

下面这篇文章是React Native 优化终极指南的一部分,它解释了为什么要重点测试 React Native 应用程序的关键部分,以便更好地了解新功能和调整。

为什么这很重要?

遵循正确的测试策略对您的产品、产品背后的团队以及您的业务都有好处。简而言之,它能让您更好地了解新功能和新调整,从而更快、更有信心地推出产品。在本文中,我们将详细介绍 React Native 中的 JavaScript 和端到端(E2E)测试。

在基于《React Native 优化终极指南》的其他博文中,我们将讨论以下与稳定性相关的主题:

请务必查看这些内容。现在,让我们进入正题。

在 React Native 应用程序中进行测试

问题: 您根本没有编写测试,或者编写的测试质量不高,没有真正的覆盖范围,只能依靠手动测试

自信地构建和部署应用程序是一项具有挑战性的任务。然而,无论是否实现了自动化,验证一切是否真正有效都需要大量的时间和精力。由专人手动验证软件是否按预期运行对产品至关重要。

遗憾的是,随着应用程序功能数量的增加,这一过程并不能很好地扩展。它也无法向编写代码的开发人员提供直接反馈。因此,发现和修复错误所需的时间会更长。

自动化测试

那么,开发人员该如何确保他们的软件始终处于生产就绪状态,而不依赖于人工测试人员呢?他们会编写自动化测试;React Native 也不例外。您可以为包含业务逻辑和用户界面的 JS 代码以及在其下方使用的本地代码编写各种测试。

您可以利用端到端测试框架、模拟器、仿真器甚至真实设备进行测试。React Native 的一大特点是它可以捆绑到原生应用捆绑包中,因此您可以在原生项目中使用您喜爱和使用的所有端到端测试框架。

但要注意的是,编写测试本身可能就是一项具有挑战性的任务,尤其是在缺乏经验的情况下。很容易出现测试不能很好地覆盖功能的情况。或者只测试正面行为,而不处理异常。低质量的测试很常见,它们不会提供太多价值,因此也不会增强你发布代码的信心。

无论你要编写哪种测试,是单元测试、集成测试还是 E2E(端到端测试的缩写)测试,都有一条黄金法则可以让你避免编写糟糕的测试。这条规则就是 "避免测试实现细节"。坚持下去,随着时间的推移,你的测试就会开始产生价值。

测试(以及缺乏测试)如何影响您的应用程序和业务

当你不能像竞争对手那样快速移动时,出现回归的几率就会很高,当你的应用程序收到差评时,就会从应用程序商店中删除。

测试代码的主要目的是最大限度地减少代码库中引入的错误数量,从而放心地部署代码。对于通常发布在应用程序商店的移动应用程序来说,不向用户提供错误信息尤为重要。

因此,它们需要经过冗长的审核过程,可能需要几天的时间。您最不希望看到的情况是,由于更新而导致应用程序出现问题,从而使用户感到沮丧。这可能会降低您的评分,在极端情况下,甚至会将应用程序从商店中下架。

这种情况看似非常罕见,但却时有发生。然后,您的团队可能会因为害怕再次出现倒退和崩溃而失去整个团队的速度和信心。

不要追求 100% 的覆盖率,而要专注于应用程序的关键部分。使用单元测试 (快照)、集成测试(排毒)。

运行测试不是 "是否 "的问题,而是 "如何 "的问题。你需要制定一个计划,说明如何让所花的时间发挥最大价值。要做到 100% 的代码行和依赖关系都覆盖到是非常困难的。而且,这往往是不切实际的。

大多数移动应用程序都不需要对代码进行全面测试。例外情况是,客户因为必须遵守政府规定而要求全面测试。但在这种情况下,您可能会意识到问题所在。

您必须集中时间测试正确的东西。学会识别关键业务特性和功能通常比编写测试本身更重要。毕竟,你要增强的是对代码的信心,而不是为了编写测试而编写测试。完成测试后,您需要做的就是决定如何运行测试。您有很多选择。

在 React Native 中,您的应用程序由多层代码组成,有些是用 JS 编写的,有些是用 Java/Kotlin 编写的,有些是用 Objective-C/Swift 编写的,有些甚至是用 C++ 编写的,而 C++ 在 React Native 核心中的应用越来越广泛。

因此,出于实际原因,我们可以区分以下几种情况:

  • JavaScript 测试 - 借助 Jest 框架。在 React Native 环境中,如果你考虑 "单元 "或 "集成 "测试,它们最终都属于这一类。从实际角度来看,没有理由区分这两类测试。

  • 端到端应用程序测试 - 借助 Detox、Appium 或其他我们熟悉的移动测试框架。

由于您的大部分业务代码都在 JS 中,因此将工作重点放在这里是合理的。

测试金字塔。来源: twitter.com/aaronabramo…

测试奖杯。来源: twitter.com/kentcdodds/…

JavaScript 测试

为实用程序函数编写测试应该非常简单。为此,您可以使用自己喜欢的测试运行程序。React Native 社区中最流行和最推荐的是 Jest

不过,要测试 React 组件,您需要更高级的工具。让我们以下面的组件为例:

function QuestionsBoard({ questions, onSubmit }) {
  const [data, setData] = React.useState({});

  return (
    <ScrollView>
      {questions.map((q, index) => {
        return (
          <View key={q}>
            <Text>{q}</Text>
            <TextInput
              accessibilityLabel="answer input"
              onChangeText={text => {
                setData(state => ({
                  ...state,
                  [index + 1]: { q, a: text },
                }));
              }}
            />
          </View>
        );
      })}
      <TouchableOpacity onPress={() => onSubmit(data)}>
        <Text>Submit</Text>
      </TouchableOpacity>
    </ScrollView>
  );
}

这是一个 React 组件,用于显示问题列表并允许回答问题。您需要通过检查回调函数是否调用了用户提供的答案集来确保其逻辑正常运行。

为此,您可以使用 React 核心团队提供的官方 react-test-renderer 库。它是一个测试渲染器;换句话说,它允许你渲染你的组件并与其生命周期交互,而无需实际处理原生 API。有些人可能会觉得它很吓人,难以使用,因为它的 API 层级很低。

因此,React Native 社区推出了一些辅助库,例如 React Native Testing Library,为我们提供了一套很好的辅助工具,帮助我们高效地编写高质量的测试。如果你想了解有关该库的更多信息,请查看 The React Native Show 中 Mike Grabowski 和 Michał Pierzchała 的详细讨论。

这个库的一大优点是,它的应用程序接口(API)迫使你避免测试组件的实现细节,从而使其对内部重构更具弹性。

QuestionsBoard 组件的测试如下:

import { render, fireEvent } from 'react-native-testing-library';
import QuestionsBoard from '../QuestionsBoard';

test('form submits two answers', () => {
  const allQuestions = ['q1', 'q2'];
  const mockFn = jest.fn();
  const { getAllByA11yLabel, getByText } = render(
    <QuestionsBoard questions={allQuestions} onSubmit={mockFn} />
  );

  const answerInputs = getAllByA11yLabel('answer input');

  fireEvent.changeText(answerInputs[0], 'a1');
  fireEvent.changeText(answerInputs[1], 'a2');
  fireEvent.press(getByText('Submit'));

  expect(mockFn).toBeCalledWith({
    '1': { q: 'q1', a: 'a1' },
    '2': { q: 'q2', a: 'a2' },
  });
});

测试套件摘自 RNTL 官方文档

首先,您将使用问题集渲染 QuestionsBoard 组件。然后,您将通过可访问性角色查询树,以访问由组件显示的问题数组。最后,您将设置正确答案并按下 "提交 "按钮。

如果一切顺利,您的断言就会通过,确保使用正确的参数集调用了 verifyQuestions 函数。

注:您可能还听说过一种名为 "快照测试 "的 JS 技术。在某些测试场景中,当重复数据从测试中被断言时,它可以帮助您。由于 Jest 的内置支持,该技术在 React 生态系统中被广泛采用。但它是一个低级的 API,除非你有丰富的测试经验,否则应避免使用。

如果您想了解有关快照测试的更多信息,请查看 Jest 网站上的 官方文档。请务必仔细阅读,因为 toMatchSnapshot 和 toMatchInlineSnapshot 是底层的 API,其中有很多问题。它们可以帮助您和您的团队快速将覆盖范围添加到项目中。但与此同时,快照也让添加低质量和难以维护的测试变得过于容易。使用eslint-plugin-jest及其no-large-snapshots选项,或snapshot-diff及其组件快照比较功能来集中断言等辅助工具,是任何使用这种测试技术的代码库的必备工具。

E2E 测试

端到端测试套件是测试金字塔顶端的樱桃。最好从所谓的 "烟雾测试 "开始,即确保应用程序在首次运行时不会崩溃的测试。进行这样的测试至关重要,因为它可以帮助您避免向用户发送有问题的应用程序。完成基础测试后,您应该使用您选择的 E2E 测试框架来测试应用程序最重要的功能。例如,登录(成功与否)、注销、接受付款、显示从自己或第三方服务器获取的数据列表。

注意:请注意,这些测试通常比 JS 测试更难设置,也更容易因各种原因(如网络、文件系统操作、存储或内存不足)而失败,而且几乎不会向您提供失败原因的信息。(不仅仅是 E2E)测试的这种特殊品质被称为 "松散性"(flakiness),您应该积极努力避免它,因为它会降低我们对测试套件的信心。这就是为什么将测试断言分成较小的组非常重要,这样更容易调试出错的地方。

本文将介绍 Detox,它是 React Native 社区中最流行的 E2E 测试运行器,也是 React Native 测试管道的一部分。使用 Detox,您可以确保您选择的框架支持最新的 React Native 版本。这一点在未来框架层面可能发生变化的情况下尤为重要。

在继续深入之前,您必须安装 Detox。在准备运行第一个套件之前,这个过程需要您采取额外的 "原生步骤"。请参考官方文档,因为这些步骤将来可能会更改。

成功安装和配置 Detox 后,您就可以开始第一次测试了。

it('should display the questions', async () => {
  await device.reloadReactNative();
  
  await element(by.text(allQuestions[0])).toBeVisible();
});

上图所示的快速测试片段将确保显示第一个问题。在执行该断言之前,您应该重新加载 React Native 实例,以确保之前的状态不会干扰结果。

注意:在处理多个元素时(例如,在我们的例子中,一个组件会渲染多个问题),一个好的做法是为元素的索引分配一个后缀 testID,以便能够查询特定的元素。官方的 Detox 推荐就是这种方法和其他一些有趣的技术。

还有各种 matchersexpectations 可供使用,帮助您按照自己想要的方式构建测试套件。

为什么值得测试 React Native 应用程序?

为核心功能提供足够覆盖范围的高质量测试套件是对数字产品成功的投资。一方面,它能让您更好地了解新功能和调整。另一方面,它还能节省时间,从而提高团队的工作速度。毕竟,只有在信心允许的情况下,你才能以最快的速度前进。而测试的目的就是确保你的方向是正确的。

React Native 社区正在努力让测试变得尽可能简单和愉快--对您的团队和质量保证团队来说都是如此。正因为如此,您可以将更多的时间用于创新,用华丽的新功能取悦用户,而不是一遍又一遍地解决错误和回归问题。


www.deepl.com 翻译