Java 测试:单元测试、集成测试、端到端测试高级水平

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

Java 测试:单元测试、集成测试、端到端测试 — 高级水平



在现代 Java 开发中,测试不仅仅是一种形式,而是一项基础实践,它确保了代码的稳定性和可预测性。诸如简单的 @Test 配合 Assert.assertEquals 这类基础测试,早已被复杂的多层策略所取代。在本文中,我们将深入探讨测试金字塔每一层的高级技术:单元测试、集成测试和端到端测试。您将学习如何编写可维护、快速且可靠的测试,让您的代码成为一个运转良好的精密机制。



1. 高级单元测试:模拟、桩和参数化



单元测试用于验证代码中一个独立的片段(通常是一个类或方法)。在高级层面,我们从简单场景转向复杂的交互和边界情况。这里的关键工具是 Mockito 库。



1.1. 使用 ArgumentCaptor 和复杂响应 (Answer)



通常,我们不仅需要验证方法是否被调用,还需要验证传递了哪些参数。这时可以使用 ArgumentCaptor。



// 示例:验证发送到服务的消息是否正确@ExtendWith(MockitoExtension.class)public class NotificationServiceTest {    @Mock    private EmailSender emailSender;    @InjectMocks    private NotificationService notificationService;

@Test void shouldSendEmailWithCorrectContent() { // 准备 User user = new User("test@example.com", "Test User"); // 执行 notificationService.sendWelcomeEmail(user); // 断言 ArgumentCaptor<EmailMessage> captor = ArgumentCaptor.forClass(EmailMessage.class); verify(emailSender).send(captor.capture()); EmailMessage capturedMessage = captor.getValue(); assertEquals("test@example.com", capturedMessage.getTo()); assertTrue(capturedMessage.getBody().contains("Test User")); }}


要基于输入数据动态创建响应,请使用 Answer 接口:



@Testvoid testDynamicResponse() {    when(dataRepository.findById(anyLong())).thenAnswer(invocation -> {        Long id = invocation.getArgument(0);        return new DataEntity(id, "Entity with ID: " + id);    });    assertEquals("Entity with ID: 5", dataService.getDataById(5L).getName());}


1.2. 参数化测试 (JUnit 5)



参数化允许使用不同的数据集运行同一个测试,从而避免代码重复。JUnit 5 提供了强大的注解,例如 @CsvSource 和 @MethodSource。



@ParameterizedTest@CsvSource({    "10, 20, 30",    "0, 0, 0",    "-5, 5, 0"})void testSum(int a, int b, int expectedSum) {    Calculator calculator = new Calculator();    assertEquals(expectedSum, calculator.add(a, b));}


2. 集成测试:与数据库和外部服务协作



集成测试用于验证系统组件之间的交互,例如与真实数据库或消息队列的交互。主要挑战在于管理环境状态。这时 Testcontainers 就派上了用场。



2.1. 使用 Testcontainers 进行隔离的数据库测试



Testcontainers 会在测试期间启动包含所需软件(PostgreSQL、Redis、Kafka)的 Docker 容器,从而保证环境的清洁和可重现性。



@SpringBootTest@Testcontainersclass UserRepositoryIntegrationTest {

@Container static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15") .withDatabaseName("testdb") .withUsername("test") .withPassword("test");

@DynamicPropertySource static void configureProperties(DynamicPropertyR

博客

书籍推荐