返回

文章详情

肯特·贝克:可组合测试

Hacker News2026年8月18日 13:08

测试愿望书期望测试具有12个属性,其中两个是:隔离——运行一个测试的结果应完全独立于其他测试的结果。组合——??? 测试应该一起运行 ??? 这难道不是和隔离相同的事情吗?不是,原因如下(我终于有了一个例子——例子总是最难的部分)。如果一个测试首先设置其自己的测试环境,完全从头创建它将用作输入的所有数据,那么该测试就保证是隔离的。无论你以什么顺序运行测试,结果都会完全相同。(这在函数式编程中是参照透明性的同一属性。)在xUnit测试框架中(至少大多数框架),通过为每个测试创建一个新的测试对象实例,并在运行测试之前先运行setUp()函数来鼓励隔离。(一些框架,尤其是NUnit,重用测试实例,从而打破隔离。)假设我们有一组孤立的测试,并将它们一起运行。此套件的成功应给予我们信心(在愿望书的术语中是可预测的),即使每个单独的测试本身并不全面。例如——假设我们有一个测试:test1() object := new Whatever() actual := object.doSomething() assertEquals(expected, actual) 我们将这段代码搞定,然后我们想实现下一个功能。我们复制、粘贴并扩展:test2() object := new Whatever() actual := object.doSomething() assertEquals(expected, actual) actual2 := object.nowSomethingElse() assertEquals(expected2, actual2) 我见过这样的测试,它们被复制、粘贴并扩展了六七遍。最后一个测试相当难以阅读。注意,如果test1失败,则test2无法通过。所有被test1捕获的不合规程序都会被test2捕获。我们至少有3个选项来保持相同的覆盖率和可预测性:保留两个测试。删除test1。简化test2。从纯粹审美的角度来看(别忽视审美),保留两个测试的状态让我感到不适。它们是冗余的!一定有什么地方错了。删除test1会失去测试愿望书中的另一个属性——测试应该是具体的。这是测试的属性,当一个测试失败时,你确切知道问题出在哪里。这导致了我更喜欢的解决方案——组合。我修剪test2以避免纯粹冗余的部分:test2() object := new Whatever() object.doSomething() actual := object.nowSomethingElse() assertEquals(expected, actual) test1 + test2的组合没有失去任何可预测的属性。它没有失去任何特定的属性。实际上,组合可能更具体,因为test1可能失败而test2可能通过(尽管它们可能因某个共同原因而都失败)。假设我们有4种计算利息的方法和5种报告该利息的方法。测试这20个测试的蛮力方法。使用组合,我们可以通过10个测试实现对系统的同样信心。如果计算利息的变体在函数式编程的意义上,与报告的变体分开,则我们需要:4个计算测试,5个报告测试,1个组合计算和报告的测试,以演示它们是连接在一起的。通过组合测试获得信心需要一些思考、推理和设计(使正交维度明显正交),但写作上的投资在制作测试中得到回报:更快,更易读,更易改变,更具体,对结构变化的敏感性更低。当我解释可组合测试的意义时,我常常会收到经验丰富的测试开发者的震惊反应。“我绝对不会减少测试中的断言。”在我看来,这似乎是基于恐惧而不是原则的反应。我们为能写测试而努力了这么久。我们不能让它们变得更糟。组合并没有让测试变得更糟。组合是从整体上看待测试,试图通过测试的几种有价值的属性来提升整体质量。通过CodeRabbit提升团队的代码质量和发布速度——为工程师打造的最先进的AI代码审查工具。CodeRabbit提供上下文感知、逐行审查、即时一键修复和简洁的PR摘要,完美融入你的GitHub工作流程,让你花更少的时间进行差异比较,更多时间进行构建。CodeRabbit提供适应你团队标准的AI驱动的审查,强制样式,发现错误和边缘情况,自动绘制代码依赖关系。具有多语言支持和超过40种静态分析工具,它保持你的代码干净、安全和可维护——无论你的技术栈多复杂。真实的案例显示了显著的影响:SalesRabbit通过简单地将CodeRabbit添加到所有部署中,减少了30%的错误,并提升了25%的工程速度。无论是帮助初级开发者还是有经验的开发者,CodeRabbit都能捕捉到即使是资深审查者可能会错过的问题。

赞助内容

NordVPN Next-gen Antivirus

本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。

请我喝杯咖啡