Unit Testing

依赖注入使代码更独立于容器,揭示 POJO 的可测试性。通过模拟对象、专门的测试支持类和 Spring 的体系结构建议,单元测试可以轻松进行,隔离代码并提高效率。模拟对象和测试支持类允许在不涉及运行时基础架构的情况下测试代码,从而提高开发效率。

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

Mock Objects

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

Environment

org.springframework.mock.env 包含`Environment` 和 PropertySource 抽象的模拟实现(见Bean Definition ProfilesPropertySource Abstraction)。MockEnvironmentMockPropertySource 对于开发对环境特定属性有依赖关系的代码的容器外测试非常有用。

JNDI

org.springframework.mock.jndi 包包含 JNDI SPI 的部分实现,你可以使用该部分实现为测试套件或独立应用程序设置一个简单的 JNDI 环境。例如,如果 JDBC DataSource 实例在测试代码中与它们在 Jakarta EE 容器中的相同 JNDI 名称绑定,你就可以在测试场景中重用应用程序代码和配置,而无需进行修改。

包中的模拟 JNDI 支持 org.springframework.mock.jndi 在 Spring Framework 5.2 中正式弃用,取而代之的是来自第三方(如 Simple-JNDI)的完整解决方案。

Servlet API

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

自 Spring 框架 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 都从与特定于服务器的实现相同的抽象基类扩展,并与它们共享行为。例如,模拟请求在创建后是不可变的,但你可以使用 ServerHttpRequest 中的 mutate() 方法创建一个已修改的实例。

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

WebTestClient 基于模拟请求和响应构建,以提供在没有 HTTP 服务器的情况下测试 WebFlux 应用程序的支持。客户端还可以用于对正在运行的服务器进行端到端测试。

Unit Testing Support Classes

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

General Testing Utilities

org.springframework.test.util 包包含几个用于单元和集成测试中的通用实用程序。

AopTestUtils 是一个 AOP 相关的实用程序方法集合。您可以使用这些方法引用隐藏在 Spring 代理之后的底层目标对象。例如,如果您使用 EasyMock 或 Mockito 等库将 Bean 配置为动态模拟,并且该模拟包含在 Spring 代理中,那么您可能需要直接访问底层模拟以配置对它的期望并执行验证。有关 Spring 的核心 AOP 实用程序,请参阅 AopUtilsAopProxyUtils

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

  • ORM 框架(如 JPA 和 Hibernate),赞成使用 privateprotected 字段访问,而不是域实体中属性的 public 设置器方法。

  • Spring 对注释的支持(如 @Autowired@Inject@Resource),这些注释为 privateprotected 字段、设置器方法和配置方法提供依赖项注入。

  • 使用诸如 @PostConstruct@PreDestroy 的注释用于生命周期回调方法。

TestSocketUtils 是一个简单的实用程序,用于发现 localhost 上可用的 TCP 端口,以便在集成测试场景中使用。

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

Spring MVC Testing Utilities

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

Unit testing Spring MVC Controllers

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