Unit Testing

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

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

Dependency injection should make your code less dependent on the container than it would be with traditional J2EE / Java EE development. The POJOs that make up your application should be testable in JUnit or TestNG tests, with objects instantiated by using the new operator, without Spring or any other container. You can use mock objects (in conjunction with other valuable testing techniques) to test your code in isolation. If you follow the architecture recommendations for Spring, the resulting clean layering and componentization of your codebase facilitate easier unit testing. For example, you can test service layer objects by stubbing or mocking DAO or repository interfaces, without needing to access persistent data while running unit tests.

真正的单元测试通常运行得非常快,因为没有运行时基础架构要设置。强调作为开发方法的一部分而进行的真正的单元测试可以提高你的工作效率。你可能不需要此测试章节部分来帮助你为基于 IoC 的应用程序编写有效的单元测试。然而,对于某些单元测试场景,Spring 框架提供了模拟对象和测试支持类,其中介绍的内容见本章节。

True unit tests typically run extremely quickly, as there is no runtime infrastructure to set up. Emphasizing true unit tests as part of your development methodology can boost your productivity. You may not need this section of the testing chapter to help you write effective unit tests for your IoC-based applications. For certain unit testing scenarios, however, the Spring Framework provides mock objects and testing support classes, which are described in this chapter.

Mock Objects

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

Spring includes a number of packages dedicated to mocking:

Environment

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

The org.springframework.mock.env package contains mock implementations of the Environment and PropertySource abstractions (see Bean Definition Profiles and PropertySource Abstraction). MockEnvironment and MockPropertySource are useful for developing out-of-container tests for code that depends on environment-specific properties.

JNDI

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

The org.springframework.mock.jndi package contains a partial implementation of the JNDI SPI, which you can use to set up a simple JNDI environment for test suites or stand-alone applications. If, for example, JDBC DataSource instances get bound to the same JNDI names in test code as they do in a Jakarta EE container, you can reuse both application code and configuration in testing scenarios without modification.

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

The mock JNDI support in the org.springframework.mock.jndi package is officially deprecated as of Spring Framework 5.2 in favor of complete solutions from third parties such as Simple-JNDI.

Servlet API

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

The org.springframework.mock.web package contains a comprehensive set of Servlet API mock objects that are useful for testing web contexts, controllers, and filters. These mock objects are targeted at usage with Spring’s Web MVC framework and are generally more convenient to use than dynamic mock objects (such as EasyMock) or alternative Servlet API mock objects (such as MockObjects).

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

Since Spring Framework 6.0, the mock objects in org.springframework.mock.web are based on the Servlet 6.0 API.

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

MockMvc builds on the mock Servlet API objects to provide an integration testing framework for Spring MVC. See MockMvc.

Spring Web Reactive

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

The org.springframework.mock.http.server.reactive package contains mock implementations of ServerHttpRequest and ServerHttpResponse for use in WebFlux applications. The org.springframework.mock.web.server package contains a mock ServerWebExchange that depends on those mock request and response objects.

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

Both MockServerHttpRequest and MockServerHttpResponse extend from the same abstract base classes as server-specific implementations and share behavior with them. For example, a mock request is immutable once created, but you can use the mutate() method from ServerHttpRequest to create a modified instance.

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

In order for the mock response to properly implement the write contract and return a write completion handle (that is, Mono<Void>), it by default uses a Flux with cache().then(), which buffers the data and makes it available for assertions in tests. Applications can set a custom write function (for example, to test an infinite stream).

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

The WebTestClient builds on the mock request and response to provide support for testing WebFlux applications without an HTTP server. The client can also be used for end-to-end tests with a running server.

Unit Testing Support Classes

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

Spring includes a number of classes that can help with unit testing. They fall into two categories:

General Testing Utilities

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

The org.springframework.test.util package contains several general purpose utilities for use in unit and integration testing.

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

AopTestUtils is a collection of AOP-related utility methods. You can use these methods to obtain a reference to the underlying target object hidden behind one or more Spring proxies. For example, if you have configured a bean as a dynamic mock by using a library such as EasyMock or Mockito, and the mock is wrapped in a Spring proxy, you may need direct access to the underlying mock to configure expectations on it and perform verifications. For Spring’s core AOP utilities, see AopUtils and AopProxyUtils.

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

ReflectionTestUtils is a collection of reflection-based utility methods. You can use these methods in testing scenarios where you need to change the value of a constant, set a non-public field, invoke a non-public setter method, or invoke a non-public configuration or lifecycle callback method when testing application code for use cases such as the following:

  • ORM frameworks (such as JPA and Hibernate) that condone private or protected field access as opposed to public setter methods for properties in a domain entity.

  • Spring’s support for annotations (such as @Autowired, @Inject, and @Resource), that provide dependency injection for private or protected fields, setter methods, and configuration methods.

  • Use of annotations such as @PostConstruct and @PreDestroy for lifecycle callback methods.

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

TestSocketUtils is a simple utility for finding available TCP ports on localhost for use in integration testing scenarios.

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

TestSocketUtils can be used in integration tests which start an external server on an available random port. However, these utilities make no guarantee about the subsequent availability of a given port and are therefore unreliable. Instead of using TestSocketUtils to find an available local port for a server, it is recommended that you rely on a server’s ability to start on a random ephemeral port that it selects or is assigned by the operating system. To interact with that server, you should query the server for the port it is currently using.

Spring MVC Testing Utilities

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

The org.springframework.test.web package contains ModelAndViewAssert, which you can use in combination with JUnit, TestNG, or any other testing framework for unit tests that deal with Spring MVC ModelAndView objects.

Unit testing Spring MVC Controllers

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

Unit testing Spring MVC Controllers

To unit test your Spring MVC Controller classes as POJOs, use ModelAndViewAssert combined with MockHttpServletRequest, MockHttpSession, and so on from Spring’s Servlet API mocks. For thorough integration testing of your Spring MVC and REST Controller classes in conjunction with your WebApplicationContext configuration for Spring MVC, use the MockMvc instead.