单元测试

依赖注入应该使您的代码对容器的依赖性降低,而不像传统的J2EE / Java EE开发那样依赖性强。构成应用程序的POJO应该可以在JUnit或TestNG测试中进行测试,对象通过使用new操作符实例化,而无需Spring或任何其他容器。您可以使用模拟对象(与其他有价值的测试技术结合使用)来对代码进行隔离测试。如果遵循Spring的架构建议,代码库的清晰分层和组件化将有助于更轻松地进行单元测试。例如,您可以通过存根化或模拟DAO或存储库接口来测试服务层对象,而无需在运行单元测试时访问持久数据。

真正的单元测试通常运行非常快,因为没有运行时基础设施需要设置。将真正的单元测试作为开发方法论的一部分强调,可以提高您的生产力。对于基于IoC的应用程序,您可能不需要本测试章节来帮助您为其编写有效的单元测试。然而,对于某些单元测试场景,Spring框架提供了模拟对象和测试支持类,这些在本章中有描述。

模拟对象

Spring包含了一些专门用于模拟的包:

环境

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

JNDI

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

Spring框架5.2版本中org.springframework.mock.jndi包中的模拟JNDI支持已正式弃用,推荐使用第三方完整解决方案,如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。

Spring MVC测试框架基于模拟Servlet API对象构建,为Spring MVC提供了一个集成测试框架。请参阅MockMvc

Spring Web Reactive

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

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

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

WebTestClient基于模拟请求和响应,提供了对WebFlux应用程序进行测试的支持,而无需HTTP服务器。该客户端也可用于与正在运行的服务器进行端到端测试。

单元测试支持类

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的MockHttpServletRequestMockHttpSessionServlet API模拟。要对Spring MVC和REST Controller类进行彻底的集成测试,结合使用Spring MVC的WebApplicationContext配置,建议使用Spring MVC 测试框架