单元测试

相比于传统的 J2EE / Java EE 开发,依赖注入应该能让你的代码减少对容器的依赖。构成应用程序的 POJO 应该可以在 JUnit 或 TestNG 测试中进行测试,对象可以通过 new 运算符实例化,而无需 Infra 或任何其他容器。你可以使用 mock 对象(结合其他有价值的测试技术)来隔离测试你的代码。如果你遵循 Infra 的架构建议,你的代码库将具有清晰的分层和组件化结构,从而更易于进行单元测试。例如,你可以通过存根(stubbing)或模拟(mocking)DAO 或 repository 接口来测试服务层对象,而无需在运行单元测试时访问持久化数据。

真正的单元测试通常运行得非常快,因为不需要设置运行时基础设施。将真正的单元测试作为开发方法的一部分加以重视,可以提高你的生产力。你可能不需要阅读本测试章节的这一部分就能帮你编写基于 IoC 的应用程序的有效单元测试。然而,对于某些单元测试场景,TODAY Framework 提供了本章中描述的 mock 对象和测试支持类。

Mock 对象

Infra 包含许多专门用于 mocking 的包:

Environment

infra.mock.env 包包含 EnvironmentPropertySource 抽象的 mock 实现(参见 Bean 定义 ProfilePropertySource 抽象)。MockEnvironmentMockPropertySource 对于开发依赖于特定环境属性的代码的容器外测试非常有用。

JNDI

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

infra.mock.jndi 包中的 mock JNDI 支持自 TODAY Framework 5.2 起已正式弃用,建议使用第三方提供的完整解决方案,如 Simple-JNDI

Mock API

infra.mock.web 包包含一套全面的 Servlet API mock 对象,对于测试 Web 上下文、控制器和过滤器非常有用。这些 mock 对象旨在与 Infra Web MVC 框架一起使用,并且通常比动态 mock 对象(如 EasyMock)或其他 Servlet API mock 对象(如 MockObjects)更易于使用。

Web MVC Test 框架建立在 mock Servlet API 对象之上,为 Web MVC 提供集成测试框架。参见 MockMvc

Infra Web Reactive

infra.mock.http.server.reactive 包包含 ServerHttpRequestServerHttpResponse 的 mock 实现,用于 WebFlux 应用程序。infra.mock.web.server 包包含一个依赖于这些 mock 请求和响应对象的 mock ServerWebExchange

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

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

WebTestClient 建立在 mock 请求和响应之上,为在没有 HTTP 服务器的情况下测试 WebFlux 应用程序提供支持。该客户端也可用于对运行中的服务器进行端到端测试。

单元测试支持类

Infra 包含许多有助于单元测试的类。它们分为两类:

通用测试工具

infra.test.util 包包含几个用于单元和集成测试的通用工具。

AopTestUtils 是一个 AOP 相关工具方法的集合。你可以使用这些方法获取隐藏在一个或多个 Infra 代理背后的底层目标对象的引用。例如,如果你使用 EasyMock 或 Mockito 等库将 bean 配置为动态 mock,并且该 mock 被包装在 Infra 代理中,你可能需要直接访问底层 mock 以对其配置期望并执行验证。有关 Infra 核心 AOP 工具,请参见 AopUtilsAopProxyUtils

ReflectionTestUtils 是一个基于反射的工具方法的集合。在测试应用程序代码时,如果你需要更改常量的值、设置非 public 字段、调用非 public setter 方法,或调用非 public 配置或生命周期回调方法,可以使用这些方法。适用场景包括:

  • ORM 框架(如 JPA 和 Hibernate),它们允许对领域实体中的属性进行 privateprotected 字段访问,而不是 public setter 方法。

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

  • 使用 @PostConstruct@PreDestroy 等注解进行生命周期回调方法。

TestSocketUtils 是一个简单的工具,用于在 localhost 上查找可用的 TCP 端口以用于集成测试场景。

TestSocketUtils 可用于启动外部服务器的集成测试,使其在随机可用端口上运行。然而,这些工具无法保证给定端口的后续可用性,因此并不可靠。建议不要使用 TestSocketUtils 为服务器查找可用本地端口,而是依赖服务器自身在操作系统选择或分配的随机临时端口上启动的能力。要与该服务器交互,你应该查询服务器当前正在使用的端口。

Web MVC 测试工具

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

单元测试 Web MVC 控制器
要将 Web MVC Controller 类作为 POJO 进行单元测试,请使用 ModelAndViewAssert 结合来自 Infra API mocksHttpMockRequestImplMockHttpSession 等。要结合 Web MVC 的 WebApplicationContext 配置对 Web MVC 和 REST Controller 类进行全面的集成测试,请改用 Web MVC Test Framework