# JUnit 最佳实践指南

> 原文： [https://howtodoinjava.com/best-practices/unit-testing-best-practices-junit-reference-guide/](https://howtodoinjava.com/best-practices/unit-testing-best-practices-junit-reference-guide/)

我假设您了解 [JUnit](http://junit.org/junit4/ "junit") 的基础知识。 如果您没有基础知识，请首先阅读（已针对 JUnit 5 更新）。 现在，我们将介绍在编写测试用例时必须考虑的 **junit [最佳实践](//howtodoinjava.com/java-best-practices/ "best practices")**。

> 编写糟糕的单元测试非常容易，这会给项目增加很少的价值，同时又会天文数字地增加代码更改的成本。

```java
Table of Contents

Unit testing is not about finding bugs
Tips for writing great unit tests
     Test only one code unit at a time
     Don’t make unnecessary assertions
     Make each test independent to all the others
     Mock out all external services and state
     Don’t unit-test configuration settings
     Name your unit tests clearly and consistently
     Write tests for methods that have the fewest dependencies first, and work your way up
     All methods, regardless of visibility, should have appropriate unit tests
     Aim for each unit test method to perform exactly one assertion
     Create unit tests that target exceptions
     Use the most appropriate assertion methods.
     Put assertion parameters in the proper order
     Ensure that test code is separated from production code
     Do not print anything out in unit tests
     Do not use static members in a test class
     Do not write your own catch blocks that exist only to fail a test
     Do not rely on indirect testing
     Integrate Testcases with build script
     Do not skip unit tests
     Capture results using the XML formatter
Summary
```

在编程中，“[**单元测试**](https://en.wikipedia.org/wiki/Unit_testing "Unit testing")是一种测试源代码的各个单元以确定它们是否适合使用的方法。” 现在，这个源代码单元可以在不同的情况下使用。

例如：在[过程编程](https://en.wikipedia.org/wiki/Procedural_programming "Procedural programming")中，一个单元可以是一个完整的模块，但更常见的是一个单独的功能或过程。 在[面向对象编程](https://en.wikipedia.org/wiki/Object-oriented_programming "Object-oriented_programming")中，一个单元通常是一个完整的接口，例如一个类，但可以是一个单独的方法。 直观上，应该将一个单元视为应用中最小的可测试部分。

## 单元测试不是寻找回归错误

好吧，了解单元测试的动机很重要。 **单元测试不是查找应用范围的错误或检测[回归](https://en.wikipedia.org/wiki/Regression_testing "Regression testing")缺陷的有效方法。 根据定义，单元测试将分别检查代码的每个单元。** 但是，当您的应用实际运行时，所有这些单元都必须协同工作，并且整个过程比独立测试部分的总和更加复杂和微妙。 证明 X 和 Y 组件都可以独立工作，并不表示它们相互兼容或配置正确。

**因此，如果您要查找回归错误，则实际上一起运行整个应用**会更有效，因为它将在生产环境中运行，就像[手动测试](https://en.wikipedia.org/wiki/Manual_testing "Manual testing") 。 如果您将这种测试自动化以检测将来发生的破损，则称为[集成测试](https://en.wikipedia.org/wiki/Integration_testing "Integration testing")，通常使用与单元测试不同的技术。

> “从本质上讲，单元测试应该被视为设计过程的一部分，因为它是 TDD（[**测试驱动开发**](https://en.wikipedia.org/wiki/Test-driven_development "Test-driven_development")）中的一部分。” 这应该用于支持设计过程，以便设计人员可以识别系统中的每个最小模块并分别进行测试。

## 编写出色的单元测试的提示

#### 1.一次仅测试一个代码单元

首先，也许是最重要的。 当我们尝试测试代码单元时，该单元可能有多个用例。 我们应该始终在单独的测试用例中测试每个用例。 例如，如果我们为一个函数编写测试用例，该函数应该具有两个参数，并且在进行一些处理后应返回一个值，那么不同的用例可能是：

1.  *第一个参数可以为空。 它应该抛出无效的参数异常。*
2.  *第二个参数可以为空。 它应该抛出无效的参数异常。*
3.  *两者都可以为空。 它应该抛出无效的参数异常。*
4.  *最后，测试功能的有效输出。 它应该返回有效的预定输出。*

当您进行了一些代码更改或重构，然后测试功能没有损坏时，这将很有帮助，运行测试用例就足够了。 同样，如果您更改任何行为，则需要更改单个或最少数量的测试用例。

#### 2.不要做出不必要的断言

请记住，单元测试是某种行为应该如何工作的设计规范，而不是对代码碰巧所做的所有事情的观察列表。

不要试图断言所有事情都只专注于要测试的内容，否则，由于一个原因，您最终将导致多个测试用例失败，这无助于实现任何目标。

#### 3.使每个测试独立于其他所有测试

不要制作单元测试用例链。 这将阻止您确定测试用例失败的根本原因，并且您将不得不调试代码。 同样，它会创建依赖关系，这意味着如果您必须更改一个测试用例，那么您就不必要在多个测试用例中进行更改。

尝试对所有测试用例使用[**`@Before`**](http://junit.sourceforge.net/javadoc/org/junit/Before.html "Junit @Before annotation")和[**`@After`**](http://junit.sourceforge.net/javadoc/org/junit/After.html "junit @After annotation")方法。 如果您需要多种支持`@Before`或`@After`中的不同测试用例的方法，请考虑创建新的`Test`类。

#### 4.模拟所有外部服务和状态

否则，这些外部服务中的行为会与多个测试重叠，并且状态数据意味着不同的单元测试会影响彼此的结果。 如果您必须按照特定的顺序运行测试，或者只有在数据库或网络连接处于活动状态时才能运行，则您肯定走错了路。

**另外，这很重要，因为您不希望调试由于某些外部系统中的错误而实际失败的测试用例。**

（顺便说一句，有时您的架构可能意味着您的代码在单元测试期间会触及静态变量。如果可以，请避免这样做，但如果不能这样做，请至少确保每个测试在运行之前将相关的静态操作重置为已知状态。 ）

#### 5.不进行单元测试配置设置

根据定义，您的配置设置不是任何代码单元的一部分（这就是为什么您将设置提取到某些属性文件中的原因）。 即使您可以编写用于检查配置的单元测试，也可以只编写一个或两个测试用例，以验证配置加载代码是否正常工作，仅此而已。

在每个单独的测试用例中测试所有配置设置仅证明了一件事：“ **您知道如何复制和粘贴**。”

#### 6.清楚一致地命名您的单元测试

好吧，这可能是最重要的一点，要记住并保持关注。 您必须根据测试案例的实际用途和测试来命名它们。 使用类名和方法名作为测试用例名称的测试用例命名约定从来都不是一个好主意。 每次更改方法名称或类名称时，您最终也会更新很多测试用例。

但是，如果您的测试用例名称是逻辑的，即基于操作，则几乎不需要修改，因为大多数应用逻辑将保持不变。

例如。 测试用例名称应类似于：

```java
1) TestCreateEmployee_NullId_ShouldThrowException
2) TestCreateEmployee_NegativeId_ShouldThrowException
3) TestCreateEmployee_DuplicateId_ShouldThrowException
4) TestCreateEmployee_ValidId_ShouldPass
```

#### 7.首先编写对依赖关系最少的方法的测试，然后逐步进行

该原则表明，如果要测试`Employee`模块，则应该首先测试`Employee`模块的创建，因为它对外部测试用例的依赖项最小。 一旦完成，就开始编写`Employee`修改的测试用例，因为它们需要一些雇员在数据库中。

要在数据库中拥有一些员工，您的创建员工测试用例必须先通过，然后才能继续。 这样，如果员工创建逻辑中存在一些错误，则可以更早地发现它。

#### 8.所有方法，无论是否可见，都应进行适当的单元测试

好吧，这确实是有争议的。 您需要查找代码中最关键的部分，并且应该对其进行测试，而不必担心它们是否是私有的。 这些方法可以具有从一到两个类调用的某些关键算法，但是它们起着重要的作用。 您想确保它们按预期工作。

#### 9.针对每种单元测试方法，以精确地执行一个断言

即使这不是经验法则，您也应该尝试在一个测试用例中仅测试一件事。 不要在单个测试用例中使用断言来测试多个事物。 这样，如果某个测试用例失败，则可以确切地知道出了什么问题。

#### 10.创建针对异常的单元测试

如果某些测试用例希望从应用中抛出[**异常**](//howtodoinjava.com/best-practices/best-practices-for-for-exception-handling/ "Best practices for Exception handling")，请使用“`Expected`”属性。 尝试避免在`catch`块中捕获异常，并使用`fail`或`asset`方法结束测试。

```java
@Test(expected=SomeDomainSpecificException.SubException.class) 

```

#### 11.使用最合适的断言方法

每个测试用例都可以使用许多断言方法。 运用最适当的理由和思想。 他们在那里是有目的的。 尊敬他们。

#### 12.以正确的顺序放置断言参数

断言方法通常采用两个参数。 一个是期望值，第二个是原始值。 根据需要依次传递它们。 如果出现问题，这将有助于正确的消息解析。

#### 13.确保测试代码与生产代码分开

在您的构建脚本中，确保测试代码未与实际源代码一起部署。 这浪费了资源。

#### 14.不要在单元测试中打印任何内容

如果您正确地遵循了所有准则，那么您将不需要在测试用例中添加任何打印语句。 如果您想拥有一个，请重新访问您的测试用例，您做错了什么。

#### 15.不要在测试类中使用静态成员。 如果您已经使用过，则针对每个测试用例重新初始化

我们已经说过，每个测试用例都应该彼此独立，因此永远不需要静态数据成员。 但是，如果您在紧急情况下需要任何帮助，请记住在执行每个测试用例之前将其重新初始化为初始值。

#### 16.不要写自己的只能使测试失败的 catch 块

如果测试代码中的任何方法引发某些异常，则不要编写`catch`块只是为了捕获异常并使测试用例失败。 而是在测试用例声明本身中使用`throws Exception`语句。 我将建议使用`Exception`类，并且不要使用`Exception`的特定子类。 这也将增加测试范围。

#### 17.不要依赖间接测试

不要假定特定的测试用例也会测试另一种情况。 这增加了歧义。 而是为每种情况编写另一个测试用例。

#### 18.将测试用例与构建脚本集成

最好将测试用例与构建脚本集成在一起，以使其在生产环境中自动执行。 这提高了应用以及测试设置的可靠性。

#### 19.不要跳过单元测试

如果某些测试用例现在无效，则将其从源代码中删除。 不要使用[**`@Ignore`**](http://junit.sourceforge.net/javadoc/org/junit/Ignore.html "Junit @Ignore annotation")或`svn.test.skip`来跳过它们的执行。 在源代码中包含无效的测试用例不会帮助任何人。

#### 20.使用 XML 格式器捕获结果

这是感觉良好的因素。 它绝对不会带来直接的好处，但是可以使运行单元测试变得有趣和有趣。 您可以将 JUnit 与 ant 构建脚本集成，并生成测试用例，并使用一些颜色编码以 XML 格式运行报告。 遵循也是一种很好的做法。

## 总结

毫无疑问，单元测试可以显着提高项目质量。 我们这个行业中的许多学者声称，任何单元测试总比没有好，但是我不同意：测试套件可以成为一项重要资产，但是不良套件可以成为负担不起的同样巨大的负担。 这取决于这些测试的质量，这似乎取决于其开发人员对单元测试的目标和原理的理解程度。

如果您理解上述准则，并尝试在下一组测试用例中实现其中的大多数准则，那么您一定会感到与众不同。

请让我知道您的想法。

学习愉快！