unit/functional 测试是否应该假定未被测试的应用程序部分运行正常?

Should unit/functional tests assume the parts of the application NOT being tested are operating correctly?

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

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

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

  1. 添加测试
  2. 运行 所有测试并验证新测试失败
  3. 编写代码使新测试通过(quick/dirty,技术债务在这里没问题)
  4. 运行 所有测试和验证全部通过
  5. 重构代码以反映最佳实践、更易于维护等。

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

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

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

当功能 (A) 中存在错误时,您对 (A) 的测试会发现它,尽管在这种情况下很难解释测试失败。

如果您在开发过程中使用 TDD,则需要考虑两个不同的“运行 测试”用例。

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

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

如果 A 是可测试的(从某种意义上说,包括 A 的实际实现不会违反我们对测试快速、可靠、确定性等的其他关注之一),并且稳定(不经常更改), 那么在测试中使用它的投资赔率很常见。

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

这里最常见的两种方法是 (a) 使用稳定的 substitute/test-double 来代替 A 所扮演的角色,或 (b) 在创建表达式时使用真实的 A用于评估B.

考虑这个简单的例子

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) returns 与 A(7)

相同的值

有用的读物​​:James Shore 的 Testing Without Mocks.