# 编写好的单元测试的 FIRST 原则

> 原文： [https://howtodoinjava.com/best-practices/first-principles-for-good-tests/](https://howtodoinjava.com/best-practices/first-principles-for-good-tests/)

在解决现实问题的任何应用中，单元测试中都存在问题 – 是最不希望的事情。 [好的书面测试](//howtodoinjava.com/best-practices/unit-testing-best-practices-junit-reference-guide/)是资产，而不好的书面测试是您应用的负担。 在本教程中，我们将学习**单元测试 FIRST 原则**，它可以使您的测试脱颖而出，并确保其收益超过成本。

## 良好单元测试的首要原则

首字母缩写词 FIRST 表示以下测试功能：

*   [F]快速
*   [I]隔离
*   [R]可重复
*   [S]自我验证
*   [T]及时

如果您在编写单元测试时遵循这五个原则，那么您将拥有更健壮的单元测试，从而使应用代码更稳定。

让我们详细了解这些 FIRST 原则。

#### 快速

**单元测试应该快速**，否则它们会减慢您的开发/部署时间，并且需要更长的时间才能通过或失败。 通常，在足够大的系统上，将有数千个单元测试 - 假设有 2000 个单元测试。 如果平均单元测试需要 200 毫秒才能运行（应该被认为是快速的），那么运行完整套件将需要 6.5 分钟。

在此阶段，6.5 分钟的时间似乎并不长，但请想象一下，如果您一天在开发计算机上多次运行它们，将会消耗大量的生产时间。 想象一下，当向应用添加新功能时，这些测试的数量增加时，它将进一步增加测试执行时间。

您的单元测试套件的价值会降低，因为它们提供有关系统运行状况的连续，全面和快速反馈的能力也会降低。

缓慢测试的主要原因之一是依赖关系，必须处理外部邪恶的必需品，例如数据库，文件和网络调用。 他们花费数千毫秒。 因此，要使套件快速运行，必须避免通过使用[模拟测试](https://howtodoinjava.com/library/mock-testing-using-powermock-with-junit-and-mockito/)创建这些依赖项。

#### 隔离的

**永远不要编写依赖于其他测试用例的测试**。 无论您如何精心设计它们，总会有误报的可能性。 更糟的是，您可能最终会花费更多的时间来确定链中的哪个测试导致了失败。

在最佳情况下，您应该可以在任何时间以任何顺序运行任何人的测试。

通过进行独立的测试，可以轻松地使测试仅关注少量行为。 如果该测试失败，您将确切知道出了什么问题以及出了什么地方。 无需调试代码本身。

[SOLID 类设计原则](//howtodoinjava.com/best-practices/5-class-design-principles-solid-in-java/)的单一责任原则（SRP）指出，类应该小而单一。 这也可以应用于您的测试。 如果您的一种测试方法可能由于多种原因而失败，请考虑将其拆分为单独的测试。

#### 可重复的

**可重复测试**每次运行都会产生相同的结果。 要完成可重复的测试，必须将它们与外部环境中的任何东西隔离开，而不是直接控制。 在这些情况下，请随意使用模拟对象。 它们就是为此目的而设计的。

有时，您需要直接与外部环境影响进行交互，例如数据库。 您需要设置一个私人沙箱，以避免与测试同时更改数据库的其他开发人员发生冲突。 在这种情况下，您可以使用[内存数据库](//howtodoinjava.com/hibernate/hibernate-4-using-in-memory-database-with-hibernate/)。

如果测试不可重复，那么您肯定会得到一些虚假的测试结果，并且您不能浪费时间去追逐幻象问题。

#### 自我验证

测试必须是自我验证的手段 – 每个测试都必须能够确定预期的输出与否。 它必须确定它失败或通过。 必须没有**人工解释结果**。

手动验证测试结果是一个耗时的过程，也可能带来更多风险。

确保您不会做任何愚蠢的事情，例如设计测试以要求在运行前需要手动安排步骤。 您必须自动执行测试所需的任何设置 – 甚至不依赖于数据库和预煮数据的存在。

创建一个内存数据库，创建架构并放入虚拟数据，然后测试代码。 这样，您可以运行该测试 N 次，而不必担心会影响测试执行及其结果的任何外部因素。

#### 及时

实际上，您可以随时编写单元测试。 您可以等待代码准备好投入生产，或者最好集中精力及时编写单元测试。

作为建议，您应该对单元测试有指导原则或严格的规则。 您可以使用审查过程甚至自动化工具来拒绝代码，而无需进行充分的测试。

您进行的单元测试越多，发现在进行相应的单元测试之前编写较小的代码块所付出的代价就越大。 首先，编写测试更加容易，其次，当您充实周围代码中的其余行为时，测试将立即获得回报。

## 额外提示

如果使用 Eclipse 或 IntelliJ IDEA，请考虑合并 [Infinitest](https://infinitest.github.io/) 之类的工具。 在对系统进行更改时，Infinitest 会识别并运行（在后台）任何可能受到影响的测试。

在更大范围内，您可以使用持续集成（CI）工具，例如 [Jenkins](https://jenkins.io/) 或 [TeamCity](https://www.jetbrains.com/teamcity/)。 CI 工具会监视您的源仓库，并在识别到更改后启动构建/测试过程。

在评论部分中，将您与 **FIRST 原则**相关的查询发送给我。

学习愉快！

> 参考：本文引用并使用了一些示例，例如 *Andy Hunt 的[“使用 JUnit 进行 Java8 中的实用单元测试”](https://pragprog.com/book/utj2/pragmatic-unit-testing-in-java-8-with-junit)中给出的示例； 杰夫·兰格，戴夫·托马斯*。