前端测试

有效的自动化代码测试策略对于确保参与Web项目支持和开发的程序员团队的快速,高质量的工作至关重要。 这篇文章的作者说,在他所工作的公司StackPath中,现在一切工作都可以很好地进行测试。 他们有许多检查代码的工具。 但是从这样的多样性中,您需要选择每种情况下最合适的。 这是一个单独的问题。 并且,在选择了必要的工具之后,您仍然需要确定其使用顺序。
图片
文章的作者说,由于应用了测试系统,StackPath对代码质量的置信度感到满意。 在这里,他想分享对公司开发的测试原理的描述,并讨论所使用的工具。

测试原理


在讨论特定工具之前,值得思考一下什么是良好测试的答案。 在为客户启动门户网站之前,我们制定并写下了在创建测试时要遵循的原则。 首先,我们所做的正是帮助我们选择工具的原因。

这是有问题的四个原则。

▍原则编号1。 测试应理解为优化任务


一个有效的测试策略是解决最大化某个值(在这种情况下,该应用程序将正常运行的置信度)和最小化某些成本(此处的“成本”由支持和运行测试所需的时间表示)的问题。 在编写测试时,我们经常会问与上述原理有关的以下问题:

  • 该测试发现错误的可能性有多大?
  • 此测试是否会改善我们的测试系统,并且编写该测试所需的资源成本是否值得从中受益?
  • 通过创建另一个更易于编写,维护和运行的测试,是否有可能获得与该测试相同的置信度?

▍原则2 应避免过度使用mox。


我在Assert.js 2018大会的演讲中对“ mok”的概念我最喜欢的解释之一是,演讲者对这个问题的讨论比我在这里要讨论的要深得多。 在演讲中,将“摩卡”的创作与“打孔现实”进行了比较。 而且我认为这是一种感知视觉的非常好的方法。 尽管我们的测试中有Mokas,但我们将由于简化了编写和运行测试过程而导致的Mokas提供的测试“成本”降低与实际导致另一个漏洞的测试价值降低进行了比较。

以前,我们的程序员非常依赖编写的单元测试,因此使用浅层酶促渲染API将所有子依赖项替换为mokas。 然后使用Jest快照检查以此方式渲染的实体。 所有这些测试都是使用类似的模式编写的:

it('renders ', () => {   const wrapper = shallow();   //  ,              expect(wrapper).toMatchSnapshot(); }); 

这些测试在许多地方都充满了现实。 这种方法非常容易通过测试实现100%的代码覆盖率。 在编写此类测试时,您几乎不需要思考,但是,如果您不检查所有众多集成点,那么此类测试就不会特别有价值。 所有测试都可以成功完成,但这对应用程序的可操作性没有太大的信心。 更糟糕的是,所有的Moka都有一个隐藏的“价格”,您必须在编写测试后支付这些价格。

▍原则3。 测试应该促进代码重构,而不是使其复杂化。


上面显示的测试使重构复杂化。 如果我发现项目中的许多地方都有重复的代码,并且过一会儿我将此代码格式化为一个单独的组件,那么使用该新组件的组件的所有测试都将失败。 使用浅层渲染技术派生的组件已经是其他东西了。 在我以前曾经重复标记的地方,现在有了一个新组件。

更复杂的重构涉及将一些组件添加到项目中并删除一些其他组件,这会导致更多的混乱。 事实是您必须向系统添加新测试,并从中删除不必要的测试。 快照再生是一项简单的任务,但是这种测试的价值是什么? 即使他们能够找到一个错误,最好还是在一系列快照更改中错过了它,并只是检查新快照而又不花太多时间,那会更好。

结果,这样的测试对重构没有特别的帮助。 理想情况下,如果执行重构,则测试不会失败,此后用户看到的内容以及与之交互的内容都不会改变。 反之亦然-如果我更改了用户的联系方式,则至少一项测试应失败。 如果测试遵循这两个规则,那么它们将是确保用户在重构期间不会发生变化的出色工具。

▍原则4。 测试应重现真实用户如何使用该应用程序


我希望测试仅在用户进行了某些更改后才能失败。 这意味着测试应与用户使用应用程序的方式相同。 例如,测试必须与表单元素进行真正的交互,并且就像用户一样,必须在文本输入字段中输入文本。 测试不应访问组件并独立调用其生命周期的方法,不应将任何内容写入组件状态,也不应执行依赖于组件实现复杂性的操作。 由于最终我要检查系统中与用户接触的部分,因此逻辑上要努力确保测试在与系统交互时尽可能重现真实用户的操作是合乎逻辑的。

测试工具


现在,我们已经定义了要实现的目标,让我们来谈谈为此选择的工具。

▍TypeScript


我们的代码库使用TypeScript。 我们的后端服务是用Go编写的,并使用gRPC相互交互。 这使我们可以生成用于GraphQL服务器的类型化gRPC客户端。 使用使用graphql-code-generator生成的类型来键入GraphQL服务器解析器。 最后,我们的查询,变异以及订阅组件和钩子均已完全键入。 使用类型完全覆盖我们的代码库可以消除由于数据形式不是程序员期望的事实而导致的一整类错误。 从schema和protobuf文件生成类型可确保我们整个系统在所使用技术堆栈的所有部分中保持同质。

▍笑话(单元测试)


作为测试代码的框架,我们使用Jest@ testing-library / react 。 在使用这些工具创建的测试中,我们将功能或组件与系统的其余部分隔离开来进行测试。 我们通常会测试应用程序中最常使用的功能和组件,或者具有多种执行代码方式的功能和组件。 在集成或端到端(E2E)测试期间,很难验证此类路径。

对我们来说,单元测试是测试小零件的一种方法。 集成和端到端测试在大规模检查系统方面做得非常出色,可让您检查应用程序运行状况的总体水平。 但是有时您需要确保这些小细节都在起作用,并且为所有可能的代码用途编写集成测试都太昂贵了。

例如,我们需要检查键盘导航在负责下拉列表的组件中是否起作用。 但是同时,我们不想在测试整个应用程序时检查此类行为的所有可能变体。 结果,我们彻底隔离地测试了导航,并且当使用适当的组件来测试页面时,我们仅关注检查高层交互。

测试工具


y赛普拉斯(整合测试)


使用赛普拉斯创建的集成测试是我们测试系统的核心。 当我们开始创建StackPath门户时,这些是我们编写的第一个测试,因为它们具有很高的价值,而创建的开销却很小。 赛普拉斯会在浏览器中显示我们的整个应用程序并运行测试脚本。 我们整个前端的工作方式与用户使用时完全相同。 诚然,系统的网络层已由mokami取代。 通常会到达GraphQL服务器的每个网络查询都将条件数据返回到应用程序。

使用模拟来模拟应用程序的网络层有很多优点:

  • 测试速度更快。 即使项目后端非常快,在整个测试套件中返回对请求的响应所需的时间也可能相当长。 如果Moki负责返回答案,则会立即返回答案。
  • 测试变得越来越可靠。 对项目执行完整的端到端测试的困难之一是必须考虑网络和服务器数据的可变状态,这些状态可能会发生变化。 如果使用Moxas模拟对网络的实际访问,则这种可变性将消失。
  • 重现需要精确重复某些条件的情况很容易。 例如,在实际系统中,很难使某些请求稳定地失败。 如果您需要检查应用程序对不成功请求的正确反应,那么moki可以轻松让您重现紧急情况。

尽管用mok替换整个后端似乎是一项艰巨的任务,但是所有条件数据的键入都使用与应用程序中使用的生成的TypeScript类型相同的类型。 也就是说,至少就结构而言,此数据至少与正常后端将返回的数据相等。 在大多数测试中,我们非常和平地忍受了使用mook代替实际服务器调用的弊端。

此外,程序员非常高兴与赛普拉斯合作。 测试在赛普拉斯测试运行器中运行。 测试说明显示在左侧,测试应用程序在主iframe元素中运行。 开始测试后,您可以研究其各个阶段,并找出应用程序一次或一次的表现。 由于运行测试的工具可以在浏览器中运行,因此您可以使用开发人员的浏览器工具来调试测试。

在编写前端测试时,通常会花费大量时间将测试的性能与测试中特定点的DOM状态进行比较。 赛普拉斯极大地简化了此任务,因为开发人员可以看到被测应用程序发生的所有事情。 这是演示此内容的视频片段。

这些测试完美地说明了我们的测试原理。 它们的价值与它们的“价格”之比适合我们。 测试非常相似地再现了真实用户与应用程序交互的动作。 只有项目的网络层被mokami取代。

▍赛普拉斯(端到端测试)


我们的E2E测试也是使用赛普拉斯编写的,但是在其中我们既不使用moki模拟项目的网络级别,也不模拟其他任何东西。 执行测试时,应用程序访问真实的GraphQL服务器,该服务器与后端服务的真实实例一起使用。

端到端测试对我们来说非常有价值。 事实是,正是这种测试的结果使我们知道某件事情是否按预期工作或不工作。 在这种测试过程中不使用任何模拟,因此,该应用程序的工作方式与真实客户端使用该程序的方式完全相同。 但是,应该注意的是,端到端测试比其他测试“更昂贵”。 考虑到在实施过程中可能会出现短期故障,因此它们运行起来较慢,编写起来更加困难。 在运行测试之前,需要做更多的工作来确保系统保持已知状态。

测试通常需要在系统处于某种已知状态时运行。 测试完成后,系统切换到另一个已知状态。 在集成测试的情况下,实现系统的这种行为并不困难,因为用mokas代替了对API的调用,因此,每个测试都在程序员控制的预定条件下运行。 但是对于E2E测试,这样做已经更加困难,因为服务器数据仓库包含在测试过程中可能会更改的信息。 结果,开发人员需要找到某种方法来确保测试开始时,系统将处于先前已知的状态。

在端到端测试运行的开始,我们运行一个脚本,该脚本通过直接调用API来创建一个具有堆栈,站点,工作负载,监视器等的新帐户。 每个测试会话都意味着使用了该帐户的新实例,但其他所有时间均保持不变。 该脚本完成了所有必要的操作后,形成一个文件,其中包含用于运行测试的数据(通常它包含有关实例标识符和域的信息)。 结果,事实证明该脚本允许您在运行测试之前使系统进入先前已知的状态。

由于端到端测试比其他类型的测试“更昂贵”,因此,与集成测试相比,我们编写的端到端测试更少。 我们努力确保测试覆盖关键的应用程序功能。 例如,这是在注册用户及其登录名,创建和设置站点/工作负载等。 由于进行了广泛的集成测试,我们知道总体上来说,我们的前端可以正常工作。 但是仅需要进行端到端测试,以确保在将前端连接到后端时不会发生其他测试无法检测到的情况。

我们全面的测试策略的缺点


尽管我们对测试和应用程序的稳定性感到非常满意,但是使用像我们这样的综合测试策略也有缺点。

首先,这种测试策略的应用意味着所有团队成员都应该熟悉许多测试工具,而不仅仅是一个。 每个人都需要了解Jest,@ testing-library / react和Cypress。 但是同时,开发人员不仅需要了解这些工具。 他们还需要能够决定在哪种情况下应使用哪种情况。 测试一些新的机会编写端到端测试是否值得,还是集成测试足够? 除了端到端或集成测试外,是否有必要编写单元测试来验证实现此新功能的小细节?

毫无疑问,这可以说“负担了我们程序员的头”,而使用唯一的工具他们不会承受这样的负担。 通常,我们从集成测试开始,然后,如果我们发现所研究的功能特别重要并且在很大程度上取决于项目的服务器部分,则可以添加适当的端到端测试。 或者我们从单元测试开始,如果我们认为单元测试将无法验证实现某种特定机制的所有细节,则可以这样做。

当然,我们仍然面临着不清楚从哪里开始的情况。 但是,随着我们不断做出关于测试的决定,常见情况的某些模式开始出现。 例如,我们通常使用单元测试来测试表单验证系统。 这样做是由于在测试期间您需要检查许多不同的情况。 同时,团队中的每个人都知道这一点,并且在其中一个需要测试表单验证系统时,不会浪费时间计划测试策略。

我们使用的方法的另一个缺点是通过测试收集有关代码覆盖率的数据的复杂性。 尽管这是可行的,但是它比用于测试项目的情况要复杂得多。 尽管通过测试追求漂亮的代码覆盖范围会导致测试质量下降,但是这些信息对于在使用的测试套件中发现“漏洞”而言非常有价值。 使用多个测试工具的问题在于,为了了解未测试代码的哪一部分,您需要将有关代码覆盖率的报告与从不同系统接收到的测试结合起来。 可以,但是绝对比阅读通过任何一种测试方法生成的报告要困难得多。

总结


当使用许多测试工具时,我们面临着艰巨的任务。 但是,每种工具都有其自己的目的。 最后,我们相信,通过将它们包含在我们的代码测试系统中,我们做了正确的事情。 集成测试-在新应用程序开始工作或为现有项目进行测试时,最好开始创建测试系统。 尝试尽早将端到端测试添加到项目中,检查项目的最重要功能将很有用。

当测试套件包含端到端和集成测试时,这应导致这样的事实,即对应用程序进行任何更改时,开发人员将对应用程序的运行状况获得一定程度的信心。 如果在项目工作过程中开始出现测试无法检测到的错误,则值得考虑哪些测试可以捕获这些错误以及错误的出现是否表明项目中使用的整个测试系统的缺陷。

当然,我们并没有立即进入当前的测试系统。 此外,我们希望随着我们项目的发展,该系统将会发展。 但是现在我们真的很喜欢我们的测试方法。

亲爱的读者们! 您在前端测试中遵循什么策略? 您使用哪些前端测试工具?


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


All Articles