React Version 16中的单元测试最佳实践

0
React Version 16中的单元测试最佳实践

随着React版本162017年,钩子现在使组件之间重用有状态逻辑成为可能。然而,有了这些新特性,工程师必须重新审视之前定义的使用React的约定。单元测试是这些实践中的一种。

网络工具平台团队的任务是创建性能网页可视化,使团队在优步先进技术集团(ATG)为自动驾驶汽车开发更好的工具,并在一定程度上实现开源软件

作为Uber ATG网络工具平台团队的暑期工程实习生,在我的团队对应用程序中的一些逻辑进行了大量修改以可视化自动驾驶车辆数据之后,我探索并定义了新的最佳实践来测试我们的各种React组件。

在使用钩子实现功能组件时,我们的团队注意到,在线上缺少React版本16单元测试的文档。在这个过程中,我们确定了使用钩子进行功能组件单元测试的各种最佳实践。我们希望其他工程团队将发现这些最佳实践是有用的,并且也可以将这些方法应用到他们的单元测试中。

测试组件的基本设置

当我开始实习时,Web工具平台团队刚刚完成了在应用程序中添加和重构各种组件,以可视化自动驾驶汽车数据。我们需要测试这些改变和新的逻辑,以确保特定的组件仍然按预期工作。在这次评估中,我们编译了测试React版本16组件的最佳实践和过程。

首先,我们使用创建组件的浅渲染,然后我们可以与快照进行比较。

创建这些效果图需要两个步骤:

  1. 类定义包装器组件的呈现。如果需要的话,包括道具。
  2. 创建一个基本的快照测试,以确保返回的JSX是正确的,并且与预期的一致。要执行快照测试,需要呈现一个组件,获取JSX输出的快照,然后将其与参考快照进行比较存储在测试中的文件。如果两个快照不匹配,则快照测试失败。

尽管快照提供了组件呈现内容的概述,但仍然有必要测试组件的内部逻辑。

测试类组件的逻辑

在React版本16中测试类组件的逻辑相当简单。开发人员可以测试类组件逻辑访问状态变量和道具,以引用特定类上下文的变量。

因此,测试类组件逻辑的第一步是检索包装器的底层类实例

测试内部函数

一般来说,我们测试了类组件中内部函数的逻辑,确保React在调用它们之后按预期修改了值。我们的团队测试了在特定条件下改变状态变量的特定函数。我们评估函数调用后变量的值是否发生了变化。

测试是否componentDidMount正确执行

在类组件中,除了渲染之外的任何其他函数都是可选的。然而,在上面的示例中,组件包含一个componentDidMount方法,它设置一个间隔,以定期更新状态字段的值,直到组件卸载为止。为了测试这个功能,我们检查了ifcomponentDidMount正确地将状态变量设置为函数。

注意,我们没有直接测试函数返回的值是什么,或者它是否正确。这个测试只确保直接函数的逻辑,componentDidMount是正确的;为了评估函数的逻辑,需要运行一个单独的测试。

测试是否componentWillUnmount正确执行

如果它被定义了,componentWillUnmount在组件最初被挂载到的DOM(文档对象模型)元素卸载之前立即调用。通常,componentWillUnmount清除组件的间隔,以便设置状态的状态变量不会在每次间隔之后更新,作为清理的一部分。在我们的测试中,我们检查确保之后componentWillUnmount时,包装器有一个未定义的intervalId,这表明它已被清除。如果包装器intervalId存在,componentWillUnmount可能无法正常工作。

功能组件

与类组件不同,我们不能使用实例测试功能组件。因为函数组件本质上只是一个函数,所以没有办法实例化一个函数,然后直接调用它的变量或函数。

为了克服这个问题,我们的最佳实践是在函数组件中分离更复杂的逻辑段,并在直接组件之外的其他文件中将它们重命名为自己的方法。

测试内部函数

的功能组件版本onOpen

为了解决这个问题,我们从onOpen到Utils文件中,并通过导出新创建的函数和使用状态变量调用来调用selectOpen和它的set方法。这样,就有可能检验逻辑onOpen

当我们测试功能组件时,例如onOpen,我们得检查一下localSetSelectOpen被称为。然而,在计算类组件时,我们可以在修改状态变量后立即确定它的值。

测试功能组件的内部功能的另一个例子是onSelectChange.最初,该函数接受参数,并确定从一组条件if语句中采取哪一种行动。现在,onSelectChange接受params并继续调用onSelectChangeLogic这是一个从Utils导出的函数,如上所述。从而进行了测试onSelectChange的原始逻辑可能作为一个独立的、可访问的函数。

在我们的两次测试中,我们都叫onSelectChangeLogic使用一组参数,其中包括模拟函数。为了确保函数按预期工作,我们检查在执行过程中是否调用了特定的模拟函数。

使用模拟钩子和函数

由于我们在测试中加入了浅渲染,并且只想测试特定的组件,而不是任何额外的子组件,因此我们利用了Jest的模拟函数编写测试。

在我们的测试中,有必要进行模拟myFunction而且mockHook能够直接调用子组件。为了检查条件1和2在前面的测试中是否不成立,我们传入模拟形参函数myFunction.因为我们的测试只检查如果myFunction调用时,我们不需要实际实现它,而是只需mockmyFunction的行为。

当我们在前面的测试中检查条件2是否存在时,就会调用一个钩子。因此,我们必须单独模拟钩子,因为React不像参数示例那样传递它。幸运的是,Jest提供了一种模拟钩子及其返回值的方法。通过这种方式,我们可以将钩子模拟为mockHook,我们在本地有一个模拟函数。

我们发现,为了保留局部变量的引用,需要给它赋值一个模拟钩子。这是因为jest.fn ()返回一个新的,未使用的模拟函数.如果钩子函数被赋值给jest.fn ()直接来说,在测试时不可能跟踪它的每个调用的引用。相比之下,通过附加模拟钩子,我们可以轻松地监视局部变量。

当我们设定myHook对于一些局部变量,我们知道我们需要用mock作为变量名的前缀(不分大小写)。这是因为jest.mock ()在模仿外部方法时,不能引用任何超出范围的变量。然而,我们意识到,只要在相关函数名的开头使用特殊关键字“mock”,就可以使用创建局部变量来跟踪某些mock函数。在变量前面加上“mock”,可以明确被模拟项应该能够访问的对象的范围。

嘲笑useEffect

useEffect是一个React内部钩子(与前面的例子不同),它在呈现之后和每次更新之后被直接调用,以执行副作用。反过来,它也可以用类似于定制钩子的方式进行测试。

这是功能组件版的componentDidMount而且componentWillUnmount来自上面类组件的示例。这个版本使用与那些实例中相同的测试方法,除了使用useEffect.为了测试发生的事情的逻辑useEffect,我们测试onEveryInterval当它从Utils导出时是单独的。

关键的外卖

在构建大型应用程序或项目时,单元测试是一个至关重要的工具,因为它允许工程师测试项目中特定结构的添加或操作是否会导致另一个结构的破坏。为了使用React第16版进行单元测试,我在Uber ATG的团队使用Enzyme围绕浅渲染创建包装器,并使用Jest模拟变量和钩子。

根据我们的经验,用React版本16测试类组件相对简单。我们只是通过检索包装器的底层类组件的实例来确保内部函数按预期操作。

另一方面,函数组件本身就是函数,因此,它们不能被实例化。为了测试这些组件,我们必须分解它们的内部逻辑,并从中创建单独的方法。然后我们使用Jest测试这些方法。

我们希望其他React开发人员也能发现这些最佳实践有帮助!

了解更多关于Uber Engineering的实习经历,请查看我们实习生的其他文章:

评论