测试React组件的便捷方法

我为Jest编写了一个自定义报告生成器,并将其发布在GitHub上 。 我的构建器叫做Jest-snapshots-book,它创建了一个HTML的React应用程序组件快照的书。



本文将讨论什么是Jest,即快照测试,它通常需要其他报表生成器以及如何编写它们。 基本上,所有这些都适用于测试React组件,但是从理论上讲,它可以在处理任何可序列化数据时应用。

分页器React组件


例如,在本文中,我们将测试分页器组件( Paginator )。 这是在AWS( GitHub )中创建无服务器应用程序的项目的一部分。 这种组件的任务是显示用于浏览表或其他页面的按钮。

这是一个简单的功能组件,没有无状态组件(无状态组件)。 作为输入,它从props接收页面总数,当前页面以及单击页面的处理程序功能。 在输出端,该组件生成一个成型的分页器。 为了显示按钮,使用了另一个子Button组件。 如果页面很多,则分页器不会显示所有页面,而是将它们组合在一起并以省略号的形式显示。



分页器组件代码
import React from 'react'; import classes from './Paginator.css'; import Button from '../../UI/Button/Button'; const Paginator = (props) => { const { tp, cp, pageClickHandler } = props; let paginator = null; if (tp !== undefined && tp > 0) { let buttons = []; buttons.push( <Button key={`pback`} disabled={cp === 1} clicked={(cp === 1 ? null : event => pageClickHandler(event, 'back'))}> ← </Button> ); const isDots = (i, tp, cp) => i > 1 && i < tp && (i > cp + 1 || i < cp - 1) && (cp > 4 || i > 5) && (cp < tp - 3 || i < tp - 4); let flag; for (let i = 1; i <= tp; i++) { const dots = isDots(i, tp, cp) && (isDots(i - 1, tp, cp) || isDots(i + 1, tp, cp)); if (flag && dots) { flag = false; buttons.push( <Button key={`p${i}`} className={classes.Dots} disabled={true}> ... </Button> ); } else if (!dots) { flag = true; buttons.push( <Button key={`p${i}`} disabled={i === cp} clicked={(i === cp ? null : event => pageClickHandler(event, i))}> {i} </Button> ); } } buttons.push( <Button key={`pforward`} disabled={cp === tp} clicked={(cp === tp ? null : event => pageClickHandler(event, 'forward'))}> → </Button> ); paginator = <div className={classes.Paginator}> {buttons} </div> } return paginator; } export default Paginator; 
按钮组件代码
 import React from 'react'; import classes from './Button.css'; const button = (props) => ( <button disabled={props.disabled} className={classes.Button + (props.className ? ' ' + props.className : '')} onClick={props.clicked}> {props.children} </button> ); export default button; 

开玩笑


Jest是一个著名的开源库,用于对JavaScript代码进行单元测试。 它的创建和开发归功于Facebook。 用Node.js编写。

一般而言,测试的含义归结为以下事实:您需要为代码提供输入参数,并立即描述代码应产生的输出。 在执行测试时,Jest使用输入参数执行代码,并根据预期结果检查结果。 如果匹配,则测试将通过,否则,测试将不通过。

来自jestjs.io的一个小例子。

假设我们有一个Node.js模块,它是一个将两个数字相加的函数( sum.js文件):

 function sum(a, b) { return a + b; } module.exports = sum; 

如果我们的模块保存在文件中以进行测试,则需要创建sum.test.js文件,在其中写入以下代码进行测试:

 const sum = require('./sum'); test('adds 1 + 2 to equal 3', () => { expect(sum(1, 2)).toBe(3); }); 

在此示例中,我们使用test函数创建了一个名为“将1 + 2等于3相加”的测试测试函数的第二个参数,我们传递一个实际执行测试的函数。

该测试包含以下事实:我们使用输入参数12执行sum函数,并将结果传递给Jest Expect ()函数。 然后,使用Jest toBe()函数,将传输的结果与预期的结果进行比较( 3 )。 toBe()函数属于Jest(匹配器)测试函数的类别。

为了进行测试,只需转到项目文件夹并在命令行上调用jest 。 Jest将找到扩展名为.test.js的文件并运行测试。 这是他将输出的结果:

 PASS ./sum.test.js ✓ adds 1 + 2 to equal 3 (5ms) 

组件的酶和快照测试


快照测试是Jest中的一个相对较新的功能。 关键是,借助特殊的测试功能,我们要求Jest将数据结构的快照保存到磁盘,然后在随后的测试运行期间,将新快照与以前保存的快照进行比较。

在这种情况下,快照不过是数据的文本表示形式。 例如,某个对象的快照将如下所示(此处的数组键是测试的名称):

 exports[`some test name`] = ` Object { "Hello": "world" } `; 

这就是Jest测试功能的样子,它执行图像比较(可选参数):

 expect(value).toMatchSnapshot(propertyMatchers, snapshotName) 

可以是任何可序列化的数据结构。 第一次, toMatchSnapshot()函数仅将快照写入磁盘;在随后的时间里,它将已经执行比较。

通常,这种测试技术专门用于测试React组件,甚至更准确地用于测试React组件的正确呈现。 为此,您需要在渲染后将组件作为传递。

是一个库,通过提供方便的组件渲染功能大大简化了对React应用程序的测试。 酶是在Airbnb开发的。

酶允许您在代码中呈现组件。 为此,有几个方便的功能可以执行不同的渲染选项:

  • 完整渲染(如浏览器中的完整DOM渲染);
  • 简化渲染(浅渲染);
  • 静态渲染

我们不会深入研究渲染选项,对于快照测试而言,静态渲染就足够了,它使您能够获取组件及其子组件的静态HTML代码:

 const wrapper = render(<Foo title="unique" />); 

因此,我们渲染组件并将结果传递给Expect() ,然后调用.toMatchSnapshot()函数。 功能只是测试功能的缩写。

 ... const wrapper = render(<Paginator tp={tp} cp={cp} />); it(`Total = ${tp}, Current = ${cp}`, () => { expect(wrapper).toMatchSnapshot(); }); ... 

每次运行测试时,toMatchSnapshot()都会比较两个快照: 预期 (先前已写入磁盘)和当前 (在当前测试期间获得)快照。

如果图片相同,则认为测试已通过。 如果图片之间存在差异,则认为未通过测试,并且以diff的形式向用户显示了两张图片之间的差异(如版本控制系统中所示)。

这是测试失败时Jest输出的示例。 在这里,我们看到当前图像中还有一个附加按钮。



在这种情况下,用户必须决定要做什么。 如果由于组件代码的更改而计划对快照进行更改,则它必须用新快照覆盖旧快照。 而且,如果更改是意外的,那么您需要在代码中查找问题。

我将给出一个测试分页器的完整示例(文件Paginator.test.js )。

为了更方便地进行分页器测试,我创建了一个快照(tp,cp)函数,该函数将使用两个参数:页面总数和当前页面。 此功能将使用给定的参数执行测试。 剩下的就是用各种参数(甚至在循环中)调用snapshoot()函数并进行测试,测试...

 import React from 'react'; import { configure, render } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import Paginator from './Paginator'; configure({ adapter: new Adapter() }); describe('Paginator', () => { const snapshoot = (tp, cp) => { const wrapper = render(<Paginator tp={tp} cp={cp} />); it(`Total = ${tp}, Current = ${cp}`, () => { expect(wrapper).toMatchSnapshot(); }); } snapshoot(0, 0); snapshoot(1, -1); snapshoot(1, 1); snapshoot(2, 2); snapshoot(3, 1); for (let cp = 1; cp <= 10; cp++) { snapshoot(10, cp); } }); 

为什么需要额外的报告生成器


当我开始使用这项测试技术时,最初方法未完成的感觉并没有离开我。 毕竟,图片只能作为文本查看。

但是,如果某些组件在渲染时会产生大量HTML代码怎么办? 这是一个三按钮分页器组件。 此类组件的快照如下所示:

 exports[`Paginator Total = 1, Current = -1 1`] = ` <div class="Paginator" > <button class="Button" > ← </button> <button class="Button" > 1 </button> <button class="Button" > → </button> </div> `; 

首先,您需要确保正确呈现组件的原始版本。 仅通过查看文本形式的HTML代码来执行此操作不是很方便。 但这只是三个按钮。 并且,如果您需要测试,例如,一张桌子或什至更大的东西? 此外,要进行全面测试,您需要查看许多图片。 这将是非常不便和困难的。

然后,如果测试失败,则需要了解组件的外观如何不同。 当然,他们的HTML代码有所不同,您将可以了解发生了什么变化,但是,再次看到个人差异的机会也不会多余。

总的来说,我认为有必要使图片可以在浏览器中以与在应用程序中相同的形式被查看。 包括适用于它们的样式。 因此,我想到了通过为Jest编写一个额外的报表生成器来改善快照测试过程的想法。

展望未来,这就是我所得到的。 每次运行测试时,构建器都会更新快照书。 您可以直接在浏览器中查看在应用程序中的组件,也可以立即查看图像和diff的源代码(如果测试失败)。



笑话报表制作工具


Jest的创建者提供了编写其他报表构建器的机会。 这样做如下。 您需要在Node.JS上编写一个模块,该模块必须具有以下一种或多种方法: onRunStartonTestStartonTestResultonRunComplete ,它们对应于各种测试进度事件。

然后,您需要在Jest配置中连接模块。 为此有一个特别的记者指令。 如果要另外包含构建器,则需要将其添加到报告器数组的末尾。

之后,Jest将在测试执行的某些阶段从模块中调用方法,并将当前结果传递给方法。 实际上,这些方法中的代码应创建您需要的其他报告。 因此,总的来说,创建其他报表构建器的过程很像。

笑话快照书的工作原理


我不会在文章中专门插入模块代码,因为我将对其进行进一步的改进。 可以在我的GitHub上找到,这是项目页面上的src / index.js文件

测试完成后,将调用我的报表生成器。 我将代码放在onRunComplete(上下文,结果)方法中。 它的工作原理如下。

estes.testResults属性中,Jest将测试结果数组传递给此函数。 每个测试结果都包含测试文件的路径和包含结果的消息数组。 我的报告生成器使用相应的快照文件搜索每个测试文件。 如果检测到快照文件,则报告构建器将在快照书中创建一个HTML页面,并将其写入项目根文件夹中的“快照书”文件夹。

为了生成HTML页面,报表构建器使用grabCSS递归函数(moduleName,css = [],级别= 0),收集所有样式,从被测组件开始,一直到其导入的所有组件树为止。 因此,该功能收集了组件正确显示所需的所有样式。 收集的样式将添加到快照书的HTML页面中。

我在项目中使用CSS模块,所以不确定如果不使用CSS模块是否可以正常工作。

如果测试通过,则构建器会将iFrame插入HTML页面,并在两个显示选项中显示图像:源代码(按原样显示图像)和渲染后的组件。 通过单击鼠标可以更改iFrame中的显示选项。

如果测试未通过,那么一切都会更加复杂。 在这种情况下,Jest仅提供它在控制台中显示的消息(请参见上面的屏幕截图)。

它包含差异和有关失败测试的其他信息。 实际上,在这种情况下,我们实际上要处理两个图像: 预期图像和实际图像。 如果我们有一个预期的快照-它存储在快照文件夹中的磁盘上,则当前的Jest快照不提供。

因此,我必须编写代码,将从消息中获取的Jest差异应用于期望的快照,并根据期望创建实际的快照。 之后,构建器在iFrame旁边显示当前快照的预期iFrame快照,可以在三个选项之间更改其内容:源代码,渲染后的组件,差异。

如果为它设置verbose = true选项,则这就是报告生成器的输出。



有用的链接



聚苯乙烯


快照测试不足以完全测试React应用程序。 它仅涵盖组件的渲染。 还必须测试其功能(例如,对用户操作的反应)。 但是,快照测试是确保组件按预期呈现的一种非常方便的方法。 而jest-snapshots-book使这个过程更加容易。

Source: https://habr.com/ru/post/zh-CN421647/


All Articles