单元测试

依赖注入应该使你的代码对容器的依赖性低于传统 J2EE / Java EE 开发。构成你应用程序的 POJO 应该能够在 JUnit 或 TestNG 测试中进行测试,通过使用 new 运算符实例化对象,而无需 Spring 或任何其他容器。你可以使用 模拟对象(结合其他有价值的测试技术)来隔离测试你的代码。如果你遵循 Spring 的架构建议,由此产生的清晰分层和代码库组件化将有助于更轻松的单元测试。例如,你可以通过存根或模拟 DAO 或存储库接口来测试服务层对象,而无需在运行单元测试时访问持久化数据。真正的单元测试通常运行得非常快,因为没有运行时基础设施需要设置。强调真正的单元测试作为你开发方法的一部分可以提高你的生产力。你可能不需要测试章节的这一部分来帮助你为基于 IoC 的应用程序编写有效的单元测试。然而,对于某些单元测试场景,Spring Framework 提供了模拟对象和测试支持类,这些将在本章中描述。

模拟对象

Spring 包含许多专门用于模拟的包:

环境

org.springframework.mock.env 包包含 EnvironmentPropertySource 抽象的模拟实现(参见 Bean 定义配置文件PropertySource 抽象)。MockEnvironmentMockPropertySource 对于为依赖于环境特定属性的代码开发容器外测试非常有用。

Servlet API

org.springframework.mock.web 包包含一套全面的 Servlet API 模拟对象,这些对象对于测试 Web 上下文、控制器和过滤器非常有用。这些模拟对象旨在与 Spring 的 Web MVC 框架一起使用,并且通常比动态模拟对象(例如 EasyMock)更方便使用。

自 Spring Framework 6.0 起,org.springframework.mock.web 中的模拟对象基于 Servlet 6.0 API。

MockMvc 构建在模拟 Servlet API 对象之上,为 Spring MVC 提供了一个集成测试框架。参见 MockMvc

Spring Web Reactive

org.springframework.mock.http.server.reactive 包包含 ServerHttpRequestServerHttpResponse 的模拟实现,用于 WebFlux 应用程序。org.springframework.mock.web.server 包包含一个模拟 ServerWebExchange,它依赖于这些模拟请求和响应对象。

MockServerHttpRequestMockServerHttpResponse 都继承自与服务器特定实现相同的抽象基类,并与它们共享行为。例如,模拟请求一旦创建就是不可变的,但你可以使用 ServerHttpRequestmutate() 方法来创建修改后的实例。

为了让模拟响应正确实现写入契约并返回写入完成句柄(即 Mono<Void>),它默认使用带有 cache().then()Flux,它缓冲数据并使其可用于测试中的断言。应用程序可以设置自定义写入函数(例如,测试无限流)。

WebTestClient 构建在模拟请求和响应之上,为无需 HTTP 服务器即可测试 WebFlux 应用程序提供支持。客户端也可以用于与正在运行的服务器进行端到端测试。

单元测试支持类

Spring 包含许多可以帮助进行单元测试的类。它们分为两类:

通用测试工具

org.springframework.test.util 包包含几个用于单元测试和集成测试的通用工具。

AopTestUtils 是 AOP 相关实用方法的集合。你可以使用这些方法来获取隐藏在一个或多个 Spring 代理后面的底层目标对象的引用。例如,如果你使用 EasyMock 或 Mockito 等库将一个 bean 配置为动态模拟,并且该模拟被 Spring 代理包装,你可能需要直接访问底层模拟以配置其预期行为并执行验证。对于 Spring 的核心 AOP 工具,参见 AopUtilsAopProxyUtils

ReflectionTestUtils 是基于反射的实用方法的集合。在以下用例中,当测试应用程序代码时,你可以在需要更改常量值、设置非 public 字段、调用非 public setter 方法或调用非 public 配置或生命周期回调方法的测试场景中使用这些方法:

  • ORM 框架(例如 JPA 和 Hibernate)允许 privateprotected 字段访问,而不是领域实体中属性的 public setter 方法。

  • Spring 对注解(例如 @Autowired@Inject@Resource)的支持,它们为 privateprotected 字段、setter 方法和配置方法提供依赖注入。

  • 使用 @PostConstruct@PreDestroy 等注解作为生命周期回调方法。

TestSocketUtils 是一个简单的实用工具,用于在 localhost 上查找可用的 TCP 端口,用于集成测试场景。

TestSocketUtils 可用于集成测试,这些测试在可用的随机端口上启动外部服务器。然而,这些实用工具不保证给定端口的后续可用性,因此不可靠。建议不要使用 TestSocketUtils 来查找服务器的可用本地端口,而是依赖服务器在其选择或由操作系统分配的随机临时端口上启动的能力。要与该服务器交互,你应该查询服务器它当前正在使用的端口。

Spring MVC 测试工具

org.springframework.test.web 包包含 ModelAndViewAssert,你可以将其与 JUnit、TestNG 或任何其他测试框架结合使用,用于处理 Spring MVC ModelAndView 对象的单元测试。

单元测试 Spring MVC 控制器

要将 Spring MVC Controller 类作为 POJO 进行单元测试,请使用 ModelAndViewAssert 结合 Spring 的 Servlet API 模拟中的 MockHttpServletRequestMockHttpSession 等。要对你的 Spring MVC 和 REST Controller 类与 Spring MVC 的 WebApplicationContext 配置进行彻底的集成测试,请改用 MockMvc