【问题标题】:Why should Mocha test cases be stateless?为什么 Mocha 测试用例应该是无状态的?
【发布时间】:2015-06-13 01:04:32
【问题描述】:

一个常见的建议是,Mocha 测试用例不应共享状态。鉴于 Mochas 执行测试用例的强顺序性,我真的不明白这个建议。还有更多 - 我认为这很可疑。

如果测试用例,即使是异步测试用例,严格地一个接一个地执行,就不会有时间竞赛问题或其他不可预测的执行顺序的风险。

让我们来看看这个 Mocha 结构:

describe
   describe
     it1
     it2    // async
     it3
   describe
     it4
     it5
describe
   describe
     it6    // async
     it7    // async
     it8
   describe
     it9
     it10

测试用例将始终以明确的顺序执行,it1、it2、... it10,独立于它们是同步还是异步,甚至独立于描述的层次结构。以下结构将产生完全相同的输出:

describe
   it1
   it2    // async
   it3
   it4
   it5
   it6    // async
   it7    // async
   it8
   it9
   it10

在单个 describe() 中的测试用例之间共享数据可以使测试用例之间的通信变得更加容易和舒适,以便:

  • 重用测试代码并避免在每个(或 其中一些)测试用例。钩子在这里很方便,但有时是 比他们提供的更多控制权
  • 减少测试执行时间 - 可以节省大量时间,避免过多的钩子,这些钩子除了满足关于无状态的建议之外什么都不做。

另一个支持在测试用例之间使用共享数据的事实是 Mocha 代码的这种简单而漂亮的结构。不同级别的 describe() 允许对该最终共享数据进行简单而清晰的范围管理。

当“全局变量”被“滥用”时,我看到的唯一不利方面是使代码更难理解、遵循和维护。无论如何,没有什么是有纪律的编码无法避免的。

这里有什么我不知道的吗?

【问题讨论】:

    标签: testing mocha.js


    【解决方案1】:

    可能使用 Mocha 来运行非无状态的测试,从而相互依赖。这不是摩卡的设计。归根结底,如果您想在测试之间施加依赖关系,那么,当然,您可以。但它有一些注意事项。

    针对错误的事情进行优化

    您引用了在测试之间共享状态的优势:

    减少测试执行时间

    当然。 在其他条件相同的情况下,我们宁愿有一个运行时间更短的测试套件。在运行整个套件时,在大型测试套件上不重置状态可能会节省相当多的时间。然而,事实是“所有其他”并不相等:

    • 运行整个套件应由自动化流程完成,该流程会报告通过和失败的内容。换句话说,不应该有人坐在那里等待整个套件完成。

    • 当出现故障或正在实施新测试时,开发人员将只想运行失败的测试或新测试,而不是整个套件。如果套件被设计为必须在运行测试 N 之前运行测试 1 到 N-1,那么开发人员等待获得他真正关心的测试结果的时间就更多了。所以他可以坐在那里,以每分钟 $X 的速度旋转他的拇指。 “多任务”不是答案,因为已经证明切换任务会产生认知成本。 (“好的,测试完成了……等等,又是什么问题?”)

    测试选择

    您可以使用--grep 选择特定测试。这对于大型测试套件非常有用,可以避免浪费时间运行您不关心的测试。所以让我们假设你的测试标题是it1,...it10,你使用--grep it7。由于 Mocha 将所有测试彼此独立,因此它将只运行适用于 it7 的任何 before 和 beforeEach 挂钩并运行测试(然后运行适用于 after 和 afterEach 的任何挂钩给它)。在运行 it7 之前,它不会运行 it1 到 it6。要让它运行这些测试,您必须制作一个涵盖所有必要测试的 --grep,这当然总是可能这样做,但并不令人愉快。

    在您在浏览器中运行 Mocha 时获得的 HTML 界面中,如果您希望 Mocha 仅运行该测试,则可以单击该测试。同样,如果您要修复一个失败的测试,这将非常有用。如果失败的测试依赖于应该在它之前运行的一堆测试,那么这个简单的点击根本就没有等价物。

    测试顺序

    一般来说,如果您的测试必须按特定顺序运行,您必须小心确保拆分到多个文件中的测试将按照您的测试运行所需的顺序加载。

    例如,可能使用全局结构在存储在不同文件中的测试之间共享状态。但是,当测试在不同的文件中时(其中一个文件不 require 另一个文件),Mocha 处理文件的顺序完全取决于文件系统列出的顺序读取包含它们的目录时的文件。对于那些想要基于字典排序的可预测顺序的人,有一个--sort 选项。如果您想强加自己的任意顺序,那么我想您必须将文件命名为 01foo.js、02bar.js 等。

    无论是使用 Node.js 运行 Mocha 还是在浏览器中运行 Mocha,都是如此。我有使用 RequireJS 加载测试文件的套件。请求模块 A、B、C 并不能保证它们会按 A、B、C 的顺序加载,除非 C 被声明为依赖于 B(也可能是 A)并且 B 依赖于 A。

    【讨论】:

    • 谢谢你,很好的解释。
    • 有时应该准备大量的数据来执行一些测试用例。这是否意味着对于其中的每一个,所有这些数据都应该批量生成然后删除?另一个例子...假设我在 beforeEach() 中打开 db-connection 并在 afterEach() 中关闭它。测试逻辑当然在 it() 中。您将如何在这 3 种方法之间传递连接句柄?或者你会简单地在每个 it() 中打开和关闭它并避免之前/之后?
    • 对于数据,例如可以在文件中预先生成数据并在测试时加载它。无需删除此数据。不过,您的系统应该重置为“已知状态”,这通常意味着从中清除数据。将数据从beforeEach“传递”到测试的常规方法是在传递给包含两者的describe 的函数范围内声明一个变量。例如,describe 的函数可以有var db,然后beforeEach 将执行db = initialize_db_connection(),it 中的测试将访问此变量。
    • 我认为这是一种传递数据的好方法(使用 describe() 作为范围),但这使得 it() 成为有状态的。 :) 无论如何,我想我明白了。感谢您的帮助。
    • 它使测试依赖于before 或beforeEach 的结果,但这就是它们存在的目的,所以没关系。取决于这些钩子不会导致我在回答中描述的任何问题。当一个测试依赖于另一个测试时,就会出现问题。如果您使用我之前描述的方法,并且碰巧一个测试依赖于另一个测试才能正常工作,那么这就是测试代码中的错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多