ReactJS测试:兔子洞有多深

大家好,我的名字叫Yaroslav Astafiev,今天我想进行一次有指导的测试ReactJS之旅。 我不会深入研究使用某些库测试Web应用程序的复杂性(以“很难仅测试不良代码的方法”为指导),作为回报,我将尝试使您的视野多样化。 因此,在本文中,React更是将测试方法放在一起的机会,这是将赶时髦的人和技术结合在一起的起点。 甚至说我们将通过ReactJS上的插图来讨论测试的原理,这将是更正确的(不仅如此)。

如果您认为自己是测试专家,请跳过本文的前半部分 ,它是关于测试的基本原理的。 如果第二部分没有为您显示任何新内容,请找我们工作并教怎么做。



如果介绍没有引起联觉发作,欢迎来猫。

单元测试


Unit Jest是用于测试JavaScript的库。 不喜欢这样-再选一个 ,但Ava更好。 一切都很简单:单击魔术按钮,并确保某个值从“ 0”更改为“ 1”:

import React from "react" import { MyButton } from "../src/components/dummy/myButton" import renderer from "react-test-renderer" test("MyButton has onPress fn", () => { let x = 0 const instance = renderer .create(<MyButton onPress={() => x++} />) .getInstance() expect(instance.handlePress).toBeDefined() expect(x).toBe(0) instance.props.handlePress() expect(x).toBe(1) }) 

现在,您具有测试魔术按钮的所有必要技能 。 不幸的是,这些技能与现实生活无关。 React组件不能很好地绝缘,隔离是单元测试的主要原理之一。 以某种方式,有必要删除以某种方式参与render方法的所有组件,但已测试的组件除外。 有一个解决方案:聪明的人为此想出了模拟API。

  // initJest.jsx file global.fetch = require('jest-fetch-mock') //custom mock const API = require('mockAPI') //static mock describe("Date() Tests", () => { beforeEach(() => { MockDate.set("2011-09-11T00:00:00.000Z") }) afterEach(() => { MockDate.reset() }) //smth ... }) 

Mock的本质很简单: Mock / Stub / Fake / Dummy / Spy等不是我们的所有。 我们在预先准备的测试数据上“模仿”我们需要组件的实际行为的方式,该行为可能具有复杂的逻辑,并相信如果输入正确的参数,所有被仿真的组件都可以正常工作。

有一个用于jestjest-fetch-mock库,您可以在其中全局定义moki。 如果您不喜欢此选项,则可以分别“润湿”测试中需要的每个组件。

同一输入上的纯函数始终返回相同的答案。 因此,如果在我们的业务逻辑中组件具有“不干净的”功能/组件,那么在单元测试中也将需要“清除”(但对于单元测试,此规则并不总是正确的)。 一个经典的例子是一个react组件,它以您需要的格式显示当前日期和时间,每次运行测试时,日期都会不同,并且您将无法编写正确的单元测试。 对于所有不同意的人,您可以使示例复杂化,在该示例中,组件应以相对格式显示日期,并以红色突出显示比当前日期早一年的日期。

因此,如果您有依赖于时间/天气/压力的动态事物,那么模拟将重新定义您需要的呼叫,以便不依赖第三方因素。 因此,您不必等待2月29日才能参加失败的考试。

单元测试规则


上述问题和解决问题的方法是尝试一种非正式的测试方法:每个测试都可以根据需要运行。 我认为,遵守三个重要的单元测试规则就足够了:

  • 决定论
  • 隔离度
  • 不受外界因素影响
  • 常识

规则一:所有测试都必须是确定性的。 如果我在Windows上编写了一个测试,那么在Mac上它也应该启动并产生相同的结果。 Windows开发人员喜欢忘记* nix系统上的文件名区分大小写。 如果测试属于CI框架,而不是生产环境中的应用,您将很幸运。

下一个规则是隔离。 我们润湿了所有未经测试的组件。 如果很难做到,那就该重构了。

最后但并非最不重要的一点:如果您的应用程序在运行时接收到数据,则还需要确定它们。 这可以是语言环境,窗口大小,日期格式,浮点数格式等。

整合测试


在我看来, 何时开始编写集成测试是一个悬而未决的问题,并且在每个团队/产品中都应考虑内部因素来做出决定。

您可以采取一种正式的方法:达到80%的单元测试覆盖率(不良的书面测试不应被审查/测试只要求包含新的或更改的代码),然后对所有书面测试进行全面审核和重构,并分析典型错误,正式制定用于编写测试的内部规则,以及每年进行这样的突袭。 如果经过上述所有操作后,您的单元测试代码覆盖率仍然是80%+,那么您的团队已经成熟,或者您根本不批评代码/测试。 如果代码覆盖率变小,那么您需要再次达到80%的覆盖率并继续编写集成测试。 您可以不那么正式地提出问题,而只需遵循常识即可:例如,对于已播放过n次的每个错误,编写测试或提出其他建议(例如扔硬币)。

第二个开放性问题: 哪些测试被视为集成 ? 也许不要回答。



在集成测试中,我们测试的不是一个组件,而是一堆中的多个组件。 没有规则,但常识告诉您:

  • 不要测试它的渲染方式,调用位置以及何时结束。
  • 不要测试ReactJS的工作,如果它不起作用,没有任何帮助。
  • 不要测试状态机React的工作方式;
  • 测试业务逻辑/数据模型/边界情况/经常中断的内容。

在此类测试中,您不应详细介绍。 它们的运行时间明显更长,并且编写它们也更加困难,因此,您不应迷失方向并涵盖应用程序逻辑中的所有次要情况。 就基础架构租赁而言,这是昂贵的,而就开发和脚本执行时间而言,则是很长的。 有人会在这个例程上度过一生,而用户管理器会很伤心 ,等待新功能,然后...

您不应该尝试测试所有内容的另一个原因是“错误安全性”(我已尝试在上面写下最重要的要点)。 每个团队都应阅读第一类和第二类错误,即诺伊曼-皮尔逊引理,并从金钱,鹦鹉或其他接受团队接受的真实性角度评估其风险。

但是该规则以及任何其他规则都有例外。 忘记上面所说的一切

  • 测试动态依赖项时。 当您不知道哪个组件会在运行时出现时,可以渲染它,但可能不会出现。 您还需要为此编写一个测试,没有人取消电路制动器。 您预期的组件错误,或者组件损坏。 因此,在这种情况下,我们编写集成测试并进行渲染。 我们检查是否一切正常,如果没有落下。
  • 使用完美像素(您知道),开发将不得不渲染和差异屏幕截图,并且每次将组件库更新到新版本时,都要更新参考屏幕截图。 因为雇用新设计师进行调和比修复容易。

快照测试


最简单的集成测试是快照:

  1. 我们拿一个组件,渲染它
  2. 在渲染器中,我们编写console.log(此)
  3. 从控制台复制参考数据
  4. 比较一下

如果您想让自己感到困惑,那么我建议您使用StoryBook库。 这是一个用于快照测试的库,它附带了StyleGuidist的思想-基于React组件创建您自己的设计系统。

快照测试的第一条规则: 从不告诉,请尝试测试数据 。 它们应该始终是静态的,“锁定的”和独立的。 第二条规则: 快照测试损坏并不意味着一切都不好 。 如果他是红色的-事实并非一切顺利。 有很多选项可以使布局相同,但是DOM树不同。 因此,我们会忽略空格,属性,键,或者我们不会测试需要那么多支持时间的内容。 或者我们用手标记出什么是坏的,什么不是。 我们修复了损坏的测试,并以模拟更新模式(该模式将测试组件渲染并在预期条件下插入Snapshot作为参考值)重新启动StoryBook。

xState和React自动机


ReactJS是一件棘手的事情。 图书馆似乎很酷。 我制作了三个组件-一个类:状态机似乎可以工作并且代码很漂亮。 然后用ReactJS编写六个月,然后看一下代码-有点废话。 您不了解拐杖在哪里,路线在哪里,州在哪里,缓存在哪里...然后您会想到:嗯,我会按照Facebook的建议去做:我会扣好“曲棍球”,“钩子”等其他东西,突然发现自己在想自己的毛呢。 ru试图从头开始寻找一个具有开发能力的项目,因此肯定可以做得很漂亮

一切都很复杂,以至于通常无法理解其工作原理。 它一直有效,直到有人抱怨为止。 我们对其进行了修复-并且它进行了修复,但是仍然存在问题。...其中一个输出是状态 ,一组确定性应用程序状态以及它们之间的允许转换。 而且,正如他们在狭窄的圈子中所说的那样,如果您没有给状态机加油,那么您就不会在反应上写东西。

值得回忆一下xState 。 这是JavaScript的确定性状态机。 可以在xState创建非常酷的UI-可以在React Automata库的文档中找到指向相应报告的链接。 反过来,React Automata是在ReactJS中修改了xState的库。 此外,她还能够为状态机的状态生成测试

如果我们的第一个复选框为true-绿灯亮。 如果第二个为假,则绘制一条灰狗,React Automata将为这些参数的所有四个组合生成测试,并验证该狗和灯泡。 没错,有时候您会希望减少一半的测试,但起初您会很高兴...无论如何,这是从侧面方便地查看您的测试,它使我非常想起确定性混乱测试的想法。

柏树


有了快照,我们或多或少地发现了,您可以迈向end2end。 我们内部拥有所有产品,因此本地解决方案是我们的唯一选择。 我希望您有机会使用云解决方案,然后像赛普拉斯这样的事情会派上用场。


以前,您选择了测试框架,使用了用于声明的库,使用库来比较复杂的XML。 然后,我们选择了一个驱动程序和一个浏览器来启动所有程序。 他们开始写了很多测试。 所有这些都吞噬了基​​础设施,您需要将所有内容塞进docker,然后拧一些东西来查看动态测试,对其进行分析,显示出什么问题...



赛普拉斯的家伙为您做了这一切。 他们解决了几个问题: 设置工作环境,编写代码,运行和编写测试。 如果测试失败,则可以显示屏幕截图,突出显示已损坏的内容。 的确,它不适用于手机,但是例如Detox 。 从入口阈值的角度来看,这当然是困难的:您将必须针对它定制应用程序,重写一堆文件等。 但是,如果您愿意,那是可能的。

软测试


有其他测试类型也不能称为良好测试。 我称它们为软测试。 例如,短绒棉。 它们主要用于前端(甚至有时灵感来自于骑手)。 有很多linterESLintJSHintPrettierStandardClinton 。 我建议Prettier:快速,便宜,开朗,易于配置,开箱即用

如果您想弄糊涂,可以配置ESLint。 让我给他一个经典的插件示例:当客户在代码中发现带有淫秽表达的注释时,他通常会发誓。 棘手的开发人员用俄语发表评论,以便客户不要猜测。 但是客户猜到了……使用Google翻译器,发现了开发人员对他的一切想法。 解决该问题的方法令人不快,可能会造成金钱或客户损失。 对于这些情况, 您始终可以为ESLint开发一个插件 ,该插件会在源代码中找到“本机俄语”字样,并说:“哦,抱歉,拒绝您的提交。”

JavaScript中的linters的优点在于可以将它们放在pre commit钩子上 。 就个人而言,我不喜欢Prettier不会存储历史记录(尽管另一方面,它也不会积累技术债务)。 从代码的统计分析的角度来看,此类测试很麻烦,因为您看不到项目的动态,因此请查看昨天,前天有多少错误。 原则上,此问题在SonarQube中解决,也在云解决方案中解决。 这是一个统计代码分析器,它存储运行的历史记录,可以使用两种语言,甚至包括PHP(还有其他不需要静态分析的铁手?:)的语言。 在其中,您可以观察漏洞,漏洞,技术债务等的动态。

复杂度测试


前端使用短绒是因为他们想要漂亮的压痕 。 复杂度也是一项软测试,您可以尝试使用它来检查代码的质量。


该图显示了我如何遵循下级提出拉取请求的想法。 在这种情况下,我建议拆除一切并修建一条直接道路。

复杂度测试遵循一个非常简单的原理:它们计算算法的循环复杂度 。 例如,他们读取一个函数,并在其中找到10个变量。 如果10-可能很难。 让我们设置复杂度1.对于每个循环,我们将为一个循环中的每个循环给出3分-9,对于一个循环中的每个循环将给出27分。我们将所有内容相加并说:循环复杂性为120,一个人只能理解6。此评估的含义是主观地说出何时需要重构源代码,将其分解,突出显示新功能等。 是的,SonarQube也可以做到。

替代测试


在我的世界中,替代测试也适用于软测试。 团结对于入职非常有用。 尽管它是用JavaScript编写的,但不仅适用于前端。 它允许您测试工作环境 。 以前,有必要编写大量指令,指出编程语言的版本,库,必要的软件列表,以便开始使用,使所有内容保持最新状态等。 现在我们可以这样说:“这是您的计算机,这是源代码。 在团结过去之前 ,不要来。” 同时,进入阈值较低。 团结可以为自定义工作环境提供指纹,并允许您添加非常简单的规则以不仅验证安装的软件。 当他们想到以下字眼时,这会激怒您:“哦,对不起,那里的某些东西对我不起作用,您能帮忙吗?..”

第二个用例(主要是用例)是使用以下单词测试生产环境 :“ CI的单元测试确实通过了,但是CI和PROD的配置差异很大。 所以没有任何保证……”。 该库的目标非常简单:实现持续集成的第一条规则:“每个人都应具有相同的环境”。 干净,隔离,以便没有副作用,或者至少没有副作用...我想欺骗谁?

API调用


碰巧的是,开发人员分为几个团队-有些写前端,有些写后端。 一个虚构的情况,不可能出现在一个真正的团队中:昨天一切正常,今天在前后发行两个版本之后一切都破裂了。 谁该怪? 作为最初有后端经验的人,我总是说:前端。 很简单,他们像往常一样把地方弄乱了。 某一时刻,前端供应商会说:“在这里,我们通过引用阅读了这篇文章,通读了指南,并学习了如何投射REST API 。 而且您不会相信它已经改变了……” 通常,如果您的后端不是Swagger,openAPI或其他类似解决方案的朋友-值得一提。

性能js


最后,是性能JS测试。 除了浏览器制造商, 没有人在测试性能JS 。 通常,它们都使用Benchmark.js 。 “哦,我们已经开发了18年的Explorer,以便它比Chrome浏览器更快地显示十亿分之一的平板电脑。” 谁需要这样的平板电脑?

如果您想进行性能测试,则最好采用另一种方法:端到端测试并观察其工作原理。 用户对应用程序整体上的工作方式形成了看法, 用户并不关心这些是后端方面的问题。

战争故事1


现在是生活中的一个例子。 老板莫名其妙地找我们说:“您的前线工作非常糟糕,几乎没有负载。 人们抱怨说,表演需要做些事情。” 我们认为:现在我们将有两个星期的时间来执行调优,选择日志记录为什么要更新 ,选择树摇晃,通过动态加载将所有内容切成碎片...如果它没有耗尽,那么我们将全部分解或变得更糟吗? 需要制定替代解决方案。 我们打开浏览器,看起来:7.5 MB,2秒,一切都很好。



放入Nginx GZip:



Nginx可以调整压缩率,让我们尝试:



增长-生产率的25%。 早点停下来 看看角落里的小设计师徽标。 即使被拉伸,它仍然非常漂亮,但是为什么我们需要它呢?



这是优化一张图片后得到的。 您可以自己评估徽标的重量。 最后,我们拜访客户说:“第一次下载不如第二次下载重要。 并且,启用强制缓存:



...每个人都快乐,每个人都欢腾!” 当然,除了用户。

因此,我们决定更频繁地进行规模审核。 Gzip,字体,图片,样式 -很少有人看到的地方,但有很多好处。

Madge和updtrJS


下一步: 依赖项审核Madge就是这样的东西,它分析代码并说:这是某某某某类与某某某某等相关的类。 如果一切都通过一个组成部分而破裂,那么将不会有什么令人愉快的事情。 Madge是出色的可视化工具,但仅适用于手动探索。 它有一个循环选项,可搜索项目中的所有循环依赖项 。 如果有的话,那是不好的,如果没有的话,那还没有写。

使用updtrJS几乎可以解决传统框架和库的难题
您有7万行代码吗? 您是否正在尝试从第13个React移到第16个? Updtr不会帮助您迁移,但是会帮助您确定可以迁移到哪些版本的库而不会造成严重后果 。 此外,它还使开发人员能够保持最新趋势,有助于保持最新的依赖性。 如果您具有良好的测试覆盖率,我建议。

静态类型


使用JS静态类型 ,因为JS动态类型根本不是功能,而是一个深渊。 流,TypeScript,reasonML就是全部...

混沌测试


混乱测试的本质很简单:启动浏览器并开始戳戳所有内容,输入在所有字段中输入的所有内容,依此类推。 直到它破裂。 Amazon, exceptions . , « , ». , . : Gremlin.com Chaos Monkey .

React — 16- , componentDidCatch. , exception, . .

Naughty String , , . , « », internal server error, ?



War Story #2



– . .

 def generateRandomPostfix(String prefix) { return prefix + "-" + Math.abs(new Random().nextInt()).toString() } def "testCorrectRandomPostfix"(){ given: def prefix = "ASD" when: def result = generateRandomPostfix(prefix) then: result?.matches(/[a-zA-Z]++-[0-9]++/) } 

. . , .

. ASD, , . . ( , groovy). Java integer 1 integer. integer integer — .





, . . ? «+» fromX. , - , XML .

.


. TestCheck.JS — . , , . — Flow-To-Gen : Flow , .

 check( property( gen.int, gen.int, (a, b) => a + b >= a && a + b >= b ) ) 

 { result: false, failingSize: 2, numTests: 3, fail: [ 2, -1 ], shrunk: { totalNodesVisited: 2, depth: 1, result: false, smallest: [ 0, -1 ] } } 

TestCheck « », . , . , , : 0 -1. 2 -1, , . !


. . , . , ? -? permission'? ? , , , , .

3rd party failures . „ “ — .

Production-


production 16- React : ErrorCeption HoneyBadger ( Sentry ). , .

Optimizely /-. , , , , , .

Out of the box


JS — . , JavaScript. — validator.w3.org/checklink . , , , , .

, , . Achecker.ca/checker . Webpagetest.org — , . Tools.pingdom.com — . www.w3.org/WAI/ER/tools .

. . , Jenkins Multibranch Plugin, - -. -, (« , »), nightly-, - regress, full regress, smart regress ..

— , , , , . , , , .

, , . .

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


All Articles