单元/功能测试是否应该假设应用程序中未被测试的部分运行正常



我是测试驱动开发的新手,但我正在开发一个新的应用程序。我已经创建了一些基本功能,但我认为这可能是开始使用测试驱动的开发的好机会。我正在编写测试需求,以涵盖使用以下基本大纲的主要功能(谢谢,维基百科(:

  1. 设置
  2. 执行
  3. 验证
  4. 清理

然后,当我开始编写测试代码时,我将遵循以下工作流程:

  1. 添加测试
  2. 运行所有测试&验证新测试失败
  3. 编写代码以使新测试通过(快速/肮脏,技术债务在这里很好(
  4. 运行所有测试&验证所有通过
  5. 重构代码以反映最佳实践、更易于维护等

我知道测试应该相互独立,但在为应用程序的特定功能(B(编写测试时,当该功能依赖于不同测试所涵盖的应用程序的不同功能(a(的功能时,就会出现问题。功能B的测试是否适合假设功能A的功能是可操作的?或者,手动执行功能A在功能B测试的测试代码中执行的任何步骤会更好吗?当功能a中出现错误时会发生什么?这会破坏两个测试,可能会导致问题的模糊性。对我来说,不允许功能B测试运行功能A似乎是正确的选择,但这样我可能会在功能B测试代码中复制功能A代码。或者测试代码可能变得太大或无法维护。

如果您将TDD作为开发过程的一部分,那么有两个不同的"运行测试";要思考的用例。

当您进行重构时,通常不关心在验证重构时有多少测试失败。如果测试次数为零,则继续进行;如果测试计数不为零,那么您可以放弃您的更改,确认您已恢复到通过状态,并尝试更加小心地再次进行更改。

合并时,事情会变得有趣得多;我们现在有许多候选更改,这些更改可以解释测试失败的原因。在这种情况下,精确的测试失败会有所帮助。

如果A是可测试的(从某种意义上说,包括A的真实实现不会违反我们对测试快速、可靠、确定性等的其他担忧(和稳定的(不会经常改变(,那么投资几率通常倾向于在测试中使用它。

当A不稳定时,特别是在A的可观察行为不稳定的情况下,我们可能需要考虑将B的测试与不稳定隔离开来的技术。

这里最常见的两种方法是(a(使用稳定的替代/测试替身来代替a所扮演的角色,或者(b(在我们创建用于评估b的表达式时使用真实的a。

考虑一下这个琐碎的示例

A(x):
return 2 * x
B(y):
if y:
return A(7)
// ...

如果A是稳定的,我们可以在编写测试时忽略它作为实现细节

assert 14 == B(true)

但描述相同行为的等效方法是使用A.的语言

assert A(7) == B(true)

读取:B(true(返回与A(7(相同的值

有用的读物:詹姆斯·肖尔的《没有模型的测试》。

至少有两种方法可以做到这一点。

  1. 正如您所说,为依赖于特性(a(的特性(B(编写一个测试。当测试运行并执行(B(的一部分时,它也会使用(A(
  2. 或者为依赖于模拟(a(的特性(B(编写一个测试。因为您的测试非常细粒度,所以您将确切地知道(A(的哪一部分将被使用,以及对(A(调用的返回值是什么。当(A(是一个很难设置的大型组件,但您将只使用其中的一小部分时,mock更有用

当功能(a(中出现错误时,(a(的测试会发现它,尽管发生这种情况时很难解释测试失败。

最新更新