对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

单元测试

依赖注入应使您的代码对容器的依赖程度低于传统的 J2EE / Java EE 开发方式。构成您应用程序的 POJO(普通 Java 对象)应当能够在 JUnit 或 TestNG 测试中进行测试,通过使用 new 操作符实例化对象,而无需依赖 Spring 或任何其他容器。您可以使用模拟对象(结合其他有价值的测试技术)来隔离地测试您的代码。 如果您遵循 Spring 的架构建议,所得到的清晰分层和组件化代码结构将有助于更轻松地进行单元测试。例如,在运行单元测试时,您可以通过对 DAO 或 Repository 接口进行桩(stubbing)或模拟(mocking),来测试服务层对象,而无需访问持久化数据。spring-doc.cadn.net.cn

真正的单元测试通常运行得非常快,因为无需设置任何运行时基础设施。在开发方法论中强调真正的单元测试可以提高你的生产力。对于基于 IoC 的应用程序编写有效的单元测试,你可能并不需要本测试章节的这一部分内容。然而,在某些单元测试场景下,Spring 框架提供了模拟对象(mock objects)和测试支持类,这些内容将在本章中进行介绍。spring-doc.cadn.net.cn

模拟对象

Spring 包含多个专门用于模拟(mocking)的包:spring-doc.cadn.net.cn

环境

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

JNDI

org.springframework.mock.jndi 包包含 JNDI SPI 的部分实现,可用于为测试套件或独立应用程序设置一个简单的 JNDI 环境。例如,如果在测试代码中将 JDBC DataSource 实例绑定到与 Jakarta EE 容器中相同的 JNDI 名称,那么你就可以在测试场景中无需修改即可重用应用程序代码和配置。spring-doc.cadn.net.cn

org.springframework.mock.jndi 包中的模拟 JNDI 支持自 Spring Framework 5.2 起已正式弃用,建议改用第三方提供的完整解决方案,例如 Simple-JNDI

Servlet API

org.springframework.mock.web 包包含了一套全面的 Servlet API 模拟对象,这些对象对于测试 Web 上下文、控制器和过滤器非常有用。这些模拟对象专为与 Spring 的 Web MVC 框架配合使用而设计,通常比动态模拟对象(例如 EasyMock)或其他 Servlet API 模拟对象(例如 MockObjects)更加方便易用。spring-doc.cadn.net.cn

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

Spring MVC 测试框架基于模拟的 Servlet API 对象,为 Spring MVC 提供了一个集成测试框架。请参阅 MockMvcspring-doc.cadn.net.cn

Spring Web Reactive

org.springframework.mock.http.server.reactive 包包含用于 WebFlux 应用程序的 ServerHttpRequestServerHttpResponse 的模拟实现。org.springframework.mock.web.server 包包含一个模拟的 ServerWebExchange,它依赖于上述模拟的请求和响应对象。spring-doc.cadn.net.cn

MockServerHttpRequestMockServerHttpResponse 均继承自与服务器特定实现相同的抽象基类,并与其共享行为。 例如,模拟请求一旦创建就是不可变的,但你可以使用 mutate() 中的 ServerHttpRequest 方法来创建一个修改后的实例。spring-doc.cadn.net.cn

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

WebTestClient 基于模拟的请求和响应构建,用于在无需 HTTP 服务器的情况下测试 WebFlux 应用程序。该客户端也可用于配合运行中的服务器进行端到端测试。spring-doc.cadn.net.cn

单元测试支持类

Spring 包含多个有助于单元测试的类。它们分为两类:spring-doc.cadn.net.cn

通用测试工具

org.springframework.test.util 包包含多个通用工具类,用于单元测试和集成测试。spring-doc.cadn.net.cn

AopTestUtils 是一组与 AOP 相关的工具方法。您可以使用这些方法获取被一个或多个 Spring 代理隐藏的底层目标对象的引用。例如,如果您使用 EasyMock 或 Mockito 等库将某个 bean 配置为动态模拟对象,且该模拟对象被包裹在 Spring 代理中,那么您可能需要直接访问底层的模拟对象,以便对其进行期望设置和执行验证。关于 Spring 核心 AOP 工具类,请参阅 AopUtilsAopProxyUtilsspring-doc.cadn.net.cn

ReflectionTestUtils 是一组基于反射的工具方法。您可以在测试场景中使用这些方法,例如需要更改常量的值、设置非public字段、调用非public的 setter 方法,或在测试应用代码时调用非public的配置或生命周期回调方法,适用于如下用例:spring-doc.cadn.net.cn

  • ORM 框架(例如 JPA 和 Hibernate)允许对领域实体中的属性使用 privateprotected 字段访问,而不是使用 public 的 setter 方法。spring-doc.cadn.net.cn

  • Spring 对注解(例如 @Autowired@Inject@Resource)的支持, 这些注解可为 privateprotected 字段、setter 方法以及配置方法提供依赖注入。spring-doc.cadn.net.cn

  • 使用诸如 @PostConstruct@PreDestroy 等注解来实现生命周期回调方法。spring-doc.cadn.net.cn

TestSocketUtils 是一个简单的实用工具,用于在 localhost 上查找可用的 TCP 端口,以便在集成测试场景中使用。spring-doc.cadn.net.cn

TestSocketUtils 可用于集成测试中,这些测试会在一个可用的随机端口上启动外部服务器。然而,这些工具类无法保证指定端口在后续仍然可用,因此并不可靠。建议不要使用 TestSocketUtils 来查找服务器可用的本地端口,而是应依赖服务器自身的能力,让其在操作系统选择或分配的一个随机临时端口(ephemeral port)上启动。要与该服务器交互时,应向服务器查询它当前正在使用的端口。spring-doc.cadn.net.cn

Spring MVC 测试工具

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

对 Spring MVC 控制器进行单元测试
若要将您的 Spring MVC Controller 类作为 POJO 进行单元测试,请结合使用 ModelAndViewAssert 以及 Spring 的 Servlet API 模拟对象 中的 MockHttpServletRequestMockHttpSession 等。若要对您的 Spring MVC 和 REST Controller 类进行全面的集成测试,并结合您的 Spring MVC WebApplicationContext 配置,请改用 Spring MVC 测试框架