是否应该仅在应用程序中断时单元测试失败



问题

您是否曾经对应用程序进行了更改并且一切运行良好,但是当您运行单元测试时,所有地狱中断都会丢失?这是我的具体问题。是否可以公平地说这些不是好的测试,因为它们在我的应用程序工作正常时会失败?通过"工作",我的意思是它通过了所有验收测试,并且每个功能都按预期工作。

背景

大多数时候,在我对应用程序进行更改后,我可以毫无问题地运行所有功能,然后当我运行单元测试时,我会遇到失败。因此,在某些情况下,我花费大量时间更新单元测试,这些单元测试对于完美运行的应用程序而言失败。我的直觉告诉我,如果你的测试失败了,当应用程序通过时,那么它们就是无用的测试,他们所做的只是提醒开发人员他们刚刚所做的实现更改。这有错吗?当相关应用程序中的其他任何内容都没有失败时,好的测试会失败吗?

示例方案

您有单元测试来验证是否返回了某个文件名,例如 fileName.txt 。在代码中的所有位置都使用常量IMPORTANT_FILENAME引用该文件,因此更改文件名不会影响应用程序。您决定将文件的名称从"文件名.txt"更改为"新文件名.txt"。您的应用程序正常工作,但您对verify(fileName,'fileName.txt')的单元测试失败。这是一个粗略的例子,但我希望你明白这一点。 这里的重点是,当我发现自己在工作应用程序上更新单元测试时,在我看来,这是一个糟糕的测试的危险信号。我想不出任何在通过应用程序中失败的测试是一个好的测试。但在我将其用作经验法则并开始删除之前,我想得到一些反馈。你同意吗?单元测试不应该在通过的应用程序中失败吗?

旁注:我认为这并不重要,但我使用Java,Junit和Mockito。

在你的例子中,是的,这是一个糟糕的测试。如果 API 合约要返回 IMPORTANT_FILENAME 的内容,则测试应该对此进行测试,而不是硬编码字符串。通常,如果测试中断,应用程序不一定会失败。单元测试的目的与其说是确保应用程序功能,不如说是强制执行 api 协定。但即使是这样,简单的逻辑规则也会告诉你"应用程序失败=>测试被破坏"并不等同于"测试被破坏=>应用程序失败"。

对我来说

,单元测试的主要目的是

  • 让我确定重构没有破坏任何东西
  • 功能更改不会产生任何不必要的副作用
  • 保护我手动测试所有内容的时间,即使用单元测试测试代码的某个部分可能很容易,但很难从外部到达
  • 经常显示设计中的缺陷(如果零件难以自动测试(
  • 强制实施编程范例

因此,如果您更改了对应用程序至关重要的内容(即更改功能(并且单元测试没有失败,那么您的单元测试就很糟糕。例如,可能会有一个单元测试来确保所有字段都是 equals/hashcode 的一部分。或者可能有单元测试来确保某些类是不可变的。您的功能测试可能仍然有效,但您引入了不需要的代码。

另一个真正重要的事情是做TDD(测试驱动开发(的可能性。

单元测试会在您无意中破坏应用程序的某些部分时提醒您。 有时,他们会给你虚惊一场。 但是,偶尔出现误报比让错误投入生产要好。

所以,是的,有时单元测试失败是可以的,因为你重新设计了单元之间的交互方式。 这并不意味着它们是糟糕的测试。 进行此类测试比在真正破坏应用程序时不收到警报要好。

如果您在不更改任何测试的情况下对应用程序进行更改,我当然希望至少有一个测试失败! 验证代码段的测试应该以某种方式中断。 其余的测试仍然应该全部通过,因为它们应该使用模拟。

因此,当某些内容发生变化时,您应该至少对测试进行一些小的更改。

但是,如果您花费太多时间来修复损坏的测试,则可能会有脆弱的测试。 如果没有看到问题代码,很难说要更改什么,但是您应该将尽可能多的软件工程原则和模式应用于测试代码,就像生产代码一样。 尽量不要复制和粘贴代码块。 通过继承、帮助程序方法或类重用代码。 这样,如果您需要进行影响许多测试的更改,则只需在一个位置进行更改。

当应用程序中没有任何内容失败或可能失败时,一个好的测试可能会失败。 通常,单元测试应涵盖可能发生的应用程序的某些方面。 但是,即使您能够编写涵盖每个场景的测试脚本(一个很大的假设(,也可能是一个好的单元测试与任何此类场景无关。 但是,谁知道是否有人可能会更改与此类测试相关的应用程序。假设您实现了一个 Map 类,但您的应用程序从不使用 remove(Key( 方法。但是,您已经实现了删除(键(单元测试。 即使您的应用程序当前从未使用 remove 方法,但有人可能会在某个时候

使用该方法。

最新更新