在2018年测试Node.js

在2018年测试Node.js

为300多个百万最终用户提供功能。 在所有这些用户都依赖我们的基础架构的情况下,我们非常擅长测试投入生产的所有产品。 我们的主要代码库是用Go编写的,剩下的是Python。

我们最近的展示应用程序Winds 2.0是使用Node.js构建的,我们很快了解到我们在Go和Python中常用的测试方法不太适合。 此外,由于我们使用的框架不提供任何类型的内置测试功能,因此创建适当的测试套件需要在Node.js中进行一些前期工作。

无论您使用哪种语言,建立一个良好的测试框架都可能很棘手。 在本文中,我们将揭露使用Node.js进行测试的难点,以及我们决定在Winds 2.0中使用的各种工具,并为正确的方向指出何时编写下一组测试。

为什么测试如此重要

我们所有人都对生产做出了糟糕的承诺,并面临后果。 发生这不是一件有趣的事情。 编写可靠的测试套件不仅是一个很好的健全性检查,而且还使您可以完全重构代码,并确信自己的代码库仍然可以运行。 如果您刚刚启动,这尤其重要。

如果您与团队一起工作,那么拥有测试覆盖面就非常重要。 没有它,团队中的其他开发人员几乎不可能知道他们的贡献是否会导致重大变化(哎呀)。

编写测试还可以鼓励您和您的团队成员将代码分成更小的部分。 这样可以更轻松地理解您的代码,并一路修正错误。 由于您尽早发现错误,因此生产率的提高甚至更大。

最后,如果没有测试,您的代码库可能就是纸牌屋。 几乎没有确定性的代码是稳定的。

坚硬的零件

我认为,我们在Winds中遇到的大多数测试问题都是Node.js特有的。 生态系统一直在增长。 例如,如果您在macOS上并运行“ brew upgrade”(安装了Homebrew),则看到Node.js新版本的机会非常高。 随着Node.js的快速发展和库的紧随其后,很难及时了解最新的库。

以下是立即想到的一些痛点:

  1. 在Node.js中进行测试是非常自以为是的,同时又不受质疑。 对于应如何构建和衡量成功的测试基础架构,许多人有不同的看法。 可悲的是,关于如何进行测试还没有黄金标准。
  2. 您的应用程序中可以使用大量框架。 但是,它们通常很小,没有明确定义的配置或引导过程。 这会导致非常常见但难以诊断的副作用。 因此,您可能最终会从头开始编写自己的测试运行程序。
  3. 几乎可以保证,您将需要编写自己的测试运行器(我们将在几分钟内完成)。

上面列出的情况并不理想,这是Node.js社区需要尽早解决而不是后来解决的问题。 如果其他语言已经弄清楚了,那么我认为现在也该是广泛采用的语言Node.js了。

编写自己的测试运行器

所以......你可能想知道一个测试运行什么。 老实说,它并不复杂。 测试运行程序是测试套件中最高的组件。 它允许您指定全局配置和环境以及导入夹具。 有人会认为这很容易做到……对吗? 没那么快…

我们了解到,尽管那里有大量的测试框架,但没有一个适用于Node.js的框架可以提供一种统一的方式来构建测试运行器。 可悲的是,这取决于开发人员。 以下是对测试跑步者的要求的快速细分:

  • 能够加载不同的配置(例如,本地,测试,开发),并确保您从不加载生产配置-您可以猜测发生这种情况时出了什么问题。
  • 使用虚拟数据提升数据库并进行播种以进行测试。 为此,这必须适用于各种数据库,无论是MySQL,PostgreSQL,MongoDB还是任何其他数据库。
  • 能够加载夹具(带有种子数据的文件,以便在开发环境中进行测试)。

在Winds中,我们选择使用Mocha作为测试运行程序。 Mocha通过命令行工具(与Babel集成)提供了一种简单的编程方式来在ES6代码库上运行测试。

为了开始测试,我们自己注册了Babel模块加载器。 这使我们可以更好地控制Babel在覆盖Node.js模块加载过程之前导入哪些模块,从而使我们有机会在运行任何测试之前模拟模块。

此外,我们还使用Mocha的测试运行器功能将HTTP处理程序预先分配给特定请求。 我们这样做是因为在测试过程中未运行正常的初始化代码(服务器交互由Chai HTTP插件模拟),并且运行了一些安全检查以确保我们未连接到生产数据库。

尽管这不是测试运行程序的一部分,但是拥有夹具装载器是我们测试套件的重要组成部分。 我们研究了现有的解决方案; 但是,我们决定编写自己的帮助程序,以便根据我们的要求进行定制。 通过我们的解决方案,我们可以在手工生成或编写夹具时遵循简单的临时约定来加载具有复杂数据相关性的夹具。

风工具

尽管该过程很麻烦,但我们能够找到适当的工具和框架之间的平衡,以使适当的测试成为我们后端API的现实。 这是我们选择使用的:

摩卡咖啡

Mocha被描述为“在Node.js上运行的功能丰富的JavaScript测试框架”,是我们直接选择的工具。 我们拥有超过1万5千颗星,许多支持者,赞助商和贡献者,我们知道这是工作的正确框架。

柴????

接下来是我们的断言库。 我们选择采用传统方法,这是最适合Mocha- Chai的方法 Chai是Node.js的BDD和TDD断言库。 通过简单的API,Chai可以轻松集成到我们的应用程序中,并允许我们轻松声明我们应该期望的结果 从Winds API返回。 最棒的是,柴的写作测试很自然。 这是一个简短的示例:

诗乃????

凭借可与任何单元测试框架一起使用的能力, Sinn是我们首选的模拟库。 再次,通过最少的设置即可实现超级干净的集成,Sinann将模拟请求变成一个简单的过程。 他们的网站拥有极其友好的用户体验,并提供了简便的步骤将Sinon与您的测试套件集成在一起。

ock

对于所有外部HTTP请求,我们使用nock ,这是一个健壮的HTTP模拟库,在您必须与第三方API(例如Stream的REST API )进行通信时,它确实非常有用。 除了它在功能上令人赞叹之外,关于这个小库没有什么可说的,这就是我们喜欢它的原因。 这是我们为Stream调用个性化引擎的简单示例:

模拟要求????

模拟请求库允许依赖外部代码。 在一行代码中,您可以替换模块,并且当某些代码尝试导入该模块时,mock-require将介入。 这是一个小型且简约但强大的库,我们是忠实拥护者。

伊斯坦布尔????

Istanbul是一个JavaScript代码覆盖率工具,可通过模块加载器挂钩计算语句,行,函数和分支的覆盖率,以在运行测试时透明地添加覆盖率。 尽管我们在CodeCov中具有类似的功能(请参阅下一节),但是在本地运行测试时,这是一个不错的工具。

最终结果-工作测试

使用所有库,包括上面提到的测试运行器,让我们看一下完整的测试是什么样的(您可以在 这里 查看我们的整个测试套件 ):

持续集成

有很多持续集成服务可用,但是我们喜欢使用Travis CI,因为它们和我们一样热爱开源环境。 鉴于Winds是开源的,因此非常适合。

我们的集成非常简单-我们有一个.travis.yml文件,该文件可以设置环境并通过简单的npm命令启动测试。 覆盖范围会报告给GitHub,在那里我们可以清楚地了解我们最新的代码库或PR是否通过了我们的测试。 GitHub集成很棒,因为无需我们去Travis CI查看结果就可以看到它。 下面是查看PR(测试后)时GitHub的屏幕截图:

在2018年测试Node.js

除了Travis CI,我们还使用一个称为CodeCov的工具。 CodeCov与Istanbul类似,但是,它是一个可视化工具,使我们可以轻松查看代码覆盖率,文件更改,行修改以及其他各种优点。 尽管无需使用CodeCov即可可视化这些数据,但是将所有内容都集中在一个位置很高兴。

我们学到了什么

在2018年测试Node.js

在开发测试套件的整个过程中,我们学到了很多东西。 由于没有“正确”的处理方式,我们决定通过对可用库进行排序来建立并创建自己的测试流程,以找到可以添加到我们工具箱中的库。

我们最终了解到,在Node.js中进行测试并不像听起来那样简单。 希望随着Node.js的不断发展,社区将团结起来,建立一个坚如磐石的库,以“正确”的方式处理与测试相关的所有事情。

在此之前,我们将继续使用我们的测试套件,该套件在Winds GitHub存储库上是开源的。

局限性

没有简单的方法来创建夹具

框架和语言(例如Python的Django)具有创建固定装置的简便方法。 例如,对于Django,您可以使用以下命令通过将数据转储到文件中来自动创建固定装置:

以下命令会将整个数据库转储到db.json文件中:
./manage.py dumpdata> db.json

以下命令将仅转储django admin.logentry表中的内容
./manage.py dumpdata admin.logentry> logentry.json

以下命令将转储django auth.user表中的内容:
./manage.py dumpdata auth.user> user.json

在Node.js中没有简单的方法来创建固定装置。 我们最终要做的是使用MongoDB Compass并从那里导出JSON。 如此产生的效果很好,如下所示(但是,这是一个乏味的过程,容易出错):

在2018年测试Node.js

使用Babel,模拟模块和Mocha Test-Runner时模块加载不直观

为了支持更广泛的节点版本并可以访问Javascript标准的最新添加,我们使用Babel将我们的ES6代码库转换为ES5。 Node.js模块系统基于CommonJS标准,而ES6模块系统具有不同的语义。

Babel在Node.js模块系统之上模拟了ES6模块的语义,但是由于我们正在通过使用模拟请求来干扰模块加载,因此我们正在着手探索奇怪的模块加载极端情况,这似乎很不直观,并可能导致多种情况。整个代码库中导入,初始化和使用的模块的独立版本。 这会使测试期间的模拟和全局状态管理变得复杂。

无法模拟在模块中使用的功能,这是在使用ES6模块时声明的

当模块导出多个函数,而一个函数调用另一个函数时,就无法模拟模块内部正在使用的函数。 原因是当您需要一个ES6模块时,会为您提供与该模块内部使用的一组独立的引用。 重新绑定引用以指向新值的任何尝试都不会真正影响模块内的代码,因为它将继续使用原始功能。

最后的想法

测试Node.js应用程序是一个复杂的过程,因为生态系统一直在发展。 掌握最新,最出色的工具非常重要,这样您就不会落伍。

如今,JavaScript相关新闻的媒体太多,很难与所有新闻保持同步。 遵循诸如JavaScript WeeklyNode Weekly这样的电子邮件新闻通讯是一个好的开始。 除此之外,加入像/ r / node这样的subreddit是一个好主意。 如果您希望掌握最新趋势,那么JS State可以在帮助开发人员可视化测试世界中的趋势方面做得很好。

最后,这是我最喜欢的几个博客,其中经常弹出文章:

认为我错过了重要的事情吗? 在评论中或在Twitter上让我知道– @NickParsons

另外,如果您想查看Stream,请在我们的网站上进行5分钟的精彩教程。 在这里一下

From: https://hackernoon.com/testing-node-js-in-2018-10a04dd77391