CodeWalk

AssertJ流式断言与Testcontainers集成测试

作者:苦行僧 · 2026-05-30 12:55

请详细解释AssertJ流式断言库的优势和常用API,以及Testcontainers如何为集成测试提供数据库/中间件等依赖的Docker容器支持。如何组合使用AssertJ和Testcontainers编写高质量集成测试?

回答

苦行僧

AssertJ流式断言

优势:可读性强、类型安全、丰富的断言方法、链式调用

import static org.assertj.core.api.Assertions.*;

// 基础断言
assertThat("Hello World")
    .isNotEmpty()
    .startsWith("Hello")
    .contains("World")
    .endsWith("ld");

// 集合断言
assertThat(list)
    .hasSize(3)
    .contains("a", "b")
    .doesNotContain("z")
    .allMatch(s -> s.length() > 0)
    .anyMatch("c"::equals);

// 异常断言
assertThatThrownBy(() -> { throw new RuntimeException("error"); })
    .isInstanceOf(RuntimeException.class)
    .hasMessage("error")
    .hasNoCause();

// Optional断言
assertThat(Optional.of("value"))
    .isPresent()
    .hasValue("value");

// 自定义比较
assertThat(person).usingRecursiveComparison()
    .ignoringFields("id", "createdAt")
    .isEqualTo(expectedPerson);

Testcontainers集成测试

核心概念:在JUnit测试中启动Docker容器,测试结束后自动销毁。

@Testcontainers
class UserRepositoryTest {
    // 静态容器(所有测试共享)
    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
        .withDatabaseName("testdb")
        .withUsername("test")
        .withPassword("test");

    // 动态属性注入
    @DynamicPropertySource
    static void configure(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }

    // 模块化容器
    @Container
    static GenericContainer<?> redis = new GenericContainer<>("redis:7-alpine")
        .withExposedPorts(6379);
}

支持的容器:PostgreSQL、MySQL、Redis、Kafka、Elasticsearch、MinIO等

组合使用

@Test
void testUserCreation() {
    // Testcontainers提供真实DB
    userService.createUser(new User("test@example.com"));
    
    // AssertJ验证结果
    assertThat(userRepository.findByEmail("test@example.com"))
        .isPresent()
        .get()
        .extracting(User::getEmail, User::isActive)
        .containsExactly("test@example.com", true);
}