单元测试
依赖注入应该使你的代码比传统的 J2EE / Java EE 开发更少依赖于容器。构成你应用程序的 POJO 应该可以在 JUnit 或 TestNG 测试中进行测试,使用 new
操作符实例化对象,而不需要 Spring 或任何其他容器。你可以使用 mock 对象(结合其他有价值的测试技术)来隔离测试你的代码。如果你遵循 Spring 的架构建议,最终得到的代码库的清晰分层和组件化将使单元测试变得更容易。例如,你可以通过存根或模拟 DAO 或存储库接口来测试服务层对象,而无需在运行单元测试时访问持久数据。
真正的单元测试通常运行得非常快,因为没有运行时基础设施需要设置。将真正的单元测试作为您开发方法的一部分可以提高您的生产力。您可能不需要本章测试部分来帮助您为基于 IoC 的应用程序编写有效的单元测试。然而,对于某些单元测试场景,Spring 框架提供了模拟对象和测试支持类,这些将在本章中描述。
Mock Objects
Spring 包含多个专门用于模拟的包:
环境
org.springframework.mock.env
包含 Environment
和 PropertySource
抽象的模拟实现(参见 Bean Definition Profiles 和 PropertySource Abstraction)。MockEnvironment
和 MockPropertySource
对于开发依赖于特定环境属性的代码的容器外测试非常有用。
Servlet API
org.springframework.mock.web
包含了一整套全面的 Servlet API 模拟对象,这些对象对于测试 web 上下文、控制器和过滤器非常有用。这些模拟对象旨在与 Spring 的 Web MVC 框架一起使用,通常比动态模拟对象(如 EasyMock)或替代的 Servlet API 模拟对象(如 MockObjects)更方便使用。
自 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
包含用于 WebFlux 应用程序的 ServerHttpRequest
和 ServerHttpResponse
的模拟实现。 org.springframework.mock.web.server
包含一个模拟的 ServerWebExchange
,它依赖于这些模拟的请求和响应对象。
MockServerHttpRequest
和 MockServerHttpResponse
都继承自相同的抽象基类,作为服务器特定实现,并与它们共享行为。例如,模拟请求一旦创建就是不可变的,但您可以使用 ServerHttpRequest
的 mutate()
方法来创建一个修改后的实例。
为了使模拟响应正确实现写入合同并返回写入完成句柄(即 Mono<Void>
),它默认使用 Flux
与 cache().then()
,这会缓存数据并使其在测试中可用于断言。应用程序可以设置自定义写入函数(例如,用于测试无限流)。
WebTestClient 基于模拟请求和响应,提供对测试 WebFlux 应用程序的支持,而无需 HTTP 服务器。该客户端还可以用于与运行中的服务器进行端到端测试。
单元测试支持类
Spring 包含了一些可以帮助单元测试的类。它们分为两类:
通用测试工具
org.springframework.test.util
包含多个通用工具,用于单元测试和集成测试。
AopTestUtils 是一组与 AOP 相关的工具方法。您可以使用这些方法获取对隐藏在一个或多个 Spring 代理后面的底层目标对象的引用。例如,如果您使用 EasyMock 或 Mockito 等库将一个 bean 配置为动态模拟,并且该模拟被包装在 Spring 代理中,您可能需要直接访问底层模拟以配置期望并执行验证。有关 Spring 核心 AOP 工具的更多信息,请参见 AopUtils 和 AopProxyUtils。
ReflectionTestUtils 是一组基于反射的工具方法。您可以在测试场景中使用这些方法,例如需要更改常量的值、设置非 public
字段、调用非 public
setter 方法,或在测试应用程序代码时调用非 public
配置或生命周期回调方法,适用于以下用例:
-
ORM 框架(如 JPA 和 Hibernate)允许对领域实体中的属性使用
private
或protected
字段访问,而不是public
设置方法。 -
Spring 对注解(如
@Autowired
、@Inject
和@Resource
)的支持,这些注解为private
或protected
字段、设置方法和配置方法提供依赖注入。 -
使用注解如
@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
结合 MockHttpServletRequest
、MockHttpSession
等来自 Spring 的 Servlet API mocks。对于您的 Spring MVC 和 REST Controller
类与 Spring MVC 的 WebApplicationContext
配置的全面集成测试,请使用 MockMvc。