本文不仅介绍了单元测试的规范,还结合实际开发案例,演示了如何编写单元测试。我们使用了 JUnit、H2、Surefire 等常用的单元测试工具。如果你希望深入了解这些工具,可以查阅相关资料。本文基于企业内部实际应用的工作流程,通过教程指导你编写符合规范的单元测试,从而提升代码质量和项目的可靠性。
下面是从 Alibaba Java 开发手册 中总结出来的单元测试规范:
- AIR 原则
好的单元测试必须遵守 AIR 原则:
- Automatic(自动化):单元测试应全自动执行,不需要手动干预。
- Independent(独立性):每个测试用例都是独立的,不依赖其他测试的结果。
- Repeatable(可重复):测试结果应始终如一,无论在什么环境下运行。
- 测试粒度要小
单元测试的粒度要足够小,通常测试到类或方法级别,这样可以精确定位问题。跨类或系统的交互逻辑应该在集成测试中处理。 - 核心代码必须测试
对于关键的业务逻辑和模块,必须编写单元测试,确保其代码在新增或修改后通过所有相关测试。 - 单元测试代码位置
单元测试代码应放在 src/test/java 目录下,而不是和业务代码混在一起,以便源码编译时可以跳过这些测试代码。 - 代码覆盖率要求
单元测试的覆盖率应达到 70% 以上,对于核心模块,语句覆盖率和分支覆盖率应达到 100%。 - BCDE 原则
编写测试时应遵循 BCDE 原则:- Border(边界):测试边界情况,如循环边界、特殊值等。
- Correct(正确):测试正确的输入和期望的输出。
- Design(设计):根据设计文档编写测试,确保实现与设计一致。
- Error(错误):测试错误输入或异常情况,确保系统能够正确处理。
- 数据库操作的测试
不要假设数据库中存在特定数据,测试时要通过代码准备测试数据。手动插入的数据可能导致测试不可靠。 - 数据库测试的清理机制
数据库相关的测试应设定自动回滚机制,避免测试数据污染数据库,或者使用有特殊前缀的标识来区分测试数据。 - 代码可测试性
对于难以测试的代码,考虑进行重构,使代码更容易进行单元测试,避免为了测试而写出不规范的代码。 - 确定测试范围
在设计评审阶段,开发人员应与测试人员一起确定单元测试的覆盖范围,确保所有重要的用例都被覆盖。 - 单元测试应在项目发布前完成
单元测试作为质量保障的手段,应在项目提测前完成,而不是项目发布后再补充。 - 编写可测代码
为了便于测试,业务代码中应避免构造方法过于复杂、全局变量过多、外部依赖过多、条件语句过多等问题。条件语句可以通过重构来简化。 - 认识单元测试的重要性
单元测试不是测试人员的职责,而是开发人员的责任。单元测试代码也是需要维护的,好的单元测试可以有效减少线上故障的发生。
通过以上总结,你可以更好地理解和应用单元测试的规范,从而编写出高质量的、可维护的测试代码,确保项目的稳定性和可靠性。特别是单元测试往往会与 CI/CD 结合,例如每次在 Githu 申请 PR 后,都会通过 Github Action 执行单元测试,确保合并代码的质量。
目录结构
下面是目录结构,在 test 文件夹中编写单元测试代码,每个单元测试以 Test 结尾。你会发现单元测试文件和代码文件一一对应。
代码实现
下面是 pom.xml 文件的内容。
我们使用的是 SpringBoot 3.1.12 和 JDK 21。我们使用 MySQL 作为数据库,使用 Spring JPA 作为 ORM。
在 maven 编译的 test 阶段,会使用 h2 内存数据库来代替 MySQL。
在 maven 编译的 build 阶段,会使用 maven-surefire-plugin 来控制单元测试的执行并在 target 文件夹中生成报告。
下面是 application.properties 的内容,用于配置 MySQL 和 JPA。如果想要运行代码,不仅仅是跑测试的话,需要创建对应的 test_db 数据库。如果仅仅跑测试的话,不需要使用的 MySQL,在测试的使用使用 H2 内存数据库。
- 创建实体类
实现一个自定义函数,为字段添加相应的限制。
- 创建 Repository 接口
实现一个自定义的数据库操作方法。
- 创建 Service 层
实现一个 service 方法,供 controller 接口使用。
- 创建 Controller 层
实现一个根据姓名查找用户的接口。
编写单元测试
下面着重介绍如何实现每个代码文件的单元测试。
- 测试 实体类
因为我们实现的实体类,包括自定义方法和相关限制,所以需要对其进行相关的测试。否则一般简单的实体类不需要测试。
下面代码通过 JUnit 5 对 类的邮箱格式验证和名称字段进行了单元测试,验证了在不同情况下(如有效或无效邮箱、空白或非空名称)类的方法是否表现正确。这些测试确保了 类在处理用户输入时的核心逻辑是可靠的。
- 测试
因为我们在 repository 中实现了自定义的数据库操作方法,所以需要对其进行测试。否则,repository 不需要进行测试。
下面代码是 的单元测试,使用 JPA 测试环境在内存数据库(H2)中验证 方法的功能。通过设置动态数据库配置,代码首先保存了两个用户对象,然后分别查找名为 “John Doe” 和 “Jane Doe” 的用户,并验证返回的用户列表是否正确。这种测试确保了 在处理数据库查询时的正确性和一致性。
- 测试
service 中的方法是必须要测试的。
下面代码是 类的单元测试,使用了 Mockito 来模拟 的依赖。测试中首先设置了模拟的行为,让 返回一个包含 用户的列表。然后调用 的 方法,并验证返回的用户列表是否正确,确保返回的用户数量和名称符合预期。此外,测试还验证了 方法在 中是否被正确调用了一次。通过这种方式,测试确保了 的逻辑在没有真实数据库依赖的情况下也能被验证。
- 测试
controller 中的接口也是必须要测试的。
下述代码是对 类的单元测试,使用了 Spring 的 注解来测试 Web 层的行为。通过 模拟 HTTP 请求,并使用 来模拟 的依赖。测试中设置了模拟行为,使得当请求 时返回一个包含 “John Doe” 的用户列表。然后,通过 模拟发送 GET 请求到 ,并验证返回的状态码为 200 OK,同时检查响应的 JSON 数据中是否正确包含用户的名称和邮箱地址。这种测试方式确保了 在处理 HTTP 请求时的行为正确性。
运行单元测试
运行单元测试的方法有下面几种:
- 通过 IDE 运行:右键点击测试类或方法,然后选择 “Run” 或 “Debug” 选项来执行测试。IDE 通常会提供一个测试结果窗口,显示测试通过、失败或被忽略的详细信息。
- 通过构建工具运行:例如 。
- 在 CI/CD 环境中自动运行:在持续集成/持续交付(CI/CD)管道中,测试通常会在每次代码提交后自动运行。CI/CD 工具(如 Jenkins、GitLab CI、Travis CI)会在构建过程中执行测试,并根据测试结果决定是否继续后续步骤。
https://github.com/rongliangtang/Spring-Boot-Demo/tree/main/demo-ut
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.mushiming.com/mjsbk/6493.html