我有一个示例项目,我在其中尝试了不同的技术。
我有以下设置:
如何使用 testcontainer-junit5 测试存储库层?
我现在的代码示例
CompanyRepositoryTest.java
:
@ExtendWith(SpringExtension.class)
@Testcontainers
public class CompanyRepositoryTest {
@Autowired
private CompanyRepository companyRepository;
@Container
public MySQLContainer mysqlContainer = new MySQLContainer()
.withDatabaseName("foo")
.withUsername("foo")
.withPassword("secret");;
@Test
public void whenFindByIdExecuted_thenNullReturned()
throws Exception {
assertEquals(companyRepository.findById(1L), Optional.ofNullable(null));
}
@Test
public void whenFindAllExecuted_thenEmptyListReturned() {
assertEquals(companyRepository.findAll(), new ArrayList<>());
}
}
当我添加
@SpringBootTest
时,我需要设置所有上下文并遇到一些应用程序加载上下文问题?
问题是,谁能揭开
@TestContainers
注释的作用吗?测试存储库时使用它的最佳实践或正确做法是什么?
@Testcontainers
注释提供的
JUnit 5扩展扫描使用
@Container
注释声明的任何容器,然后启动和停止这些容器以进行测试。作为静态字段的容器将与所有测试共享,并且作为实例字段的容器将为每个测试启动和停止。
如果您使用 Spring Boot,为测试设置测试容器的最简单方法可能是在
application-test.yml
中提供属性。这将使用数据源 JDBC URL 来启动 testcontainers 容器。请参阅测试容器JDBC 支持了解更多信息。
您还可以使用
@DataJpaTest
而不是 @SpringBootTest
来仅测试存储库层:
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@ActiveProfiles("test")
class CompanyRepositoryTest { }
您的
application-test.yml
文件:
spring:
datasource:
url: jdbc:tc:mysql:8.0://hostname/databasename
driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver
在某些情况下,您可能还想使用
@TestPropertySource
注释来代替:
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestPropertySource(
properties = {
"spring.datasource.url = jdbc:tc:mysql:8.0://hostname/test-database",
"spring.datasource.driver-class-name = org.testcontainers.jdbc.ContainerDatabaseDriver"
}
)
class CompanyRepositoryTest { }
请注意,
hostname
和test-database
实际上并没有在任何地方使用。
你说
当我添加@SpringBootTest时,我需要设置所有上下文并拥有 一些应用程序加载上下文问题?
如果您想尝试替代方案并且 Testcontainer 不是强制性的,您可以采用不同的方式。
使用SpringBootTest注解时不需要加载所有的类,可以指定需要哪些类比如
@SpringBootTest(classes = { TheService.class })
或使用
@Import
注释
并嘲笑其他人,例如
@MockBean
MyService service;
对于数据库连接,您可以使用注释,例如
@ActiveProfiles("my-profile-for-jpa-test")
@DataJpaTest
@EnableJpaAuditing
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
编辑:我觉得这应该是一条评论,但我想用正确的格式来解决问题的 SpringBootTest 部分
这是一个示例,我如何在 Spring 中使用 MySql 配置 Liquibase(与 Flyway 类似的框架):
@DataJpaTest
@TestPropertySource(properties = {"spring.jpa.hibernate.ddl-auto=validate"})
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@ContextConfiguration(initializers = { MySqlLiquibaseBaseIT.Initializer.class })
@Testcontainers
public class MySqlLiquibaseBaseIT {
@Container
public static MySQLContainer<?> mysql = new MySQLContainer<>(
DockerImageName
.parse(MySQLContainer.NAME)
.withTag("5.7.22"));
@Configuration
@EnableJpaRepositories
@EntityScan
static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
TestPropertyValues.of(
"spring.datasource.url=" + mysql.getJdbcUrl(),
"spring.datasource.username=" + mysql.getUsername(),
"spring.datasource.password=" + mysql.getPassword(),
"spring.datasource.driver-class-name=" + mysql.getDriverClassName())
.applyTo(configurableApplicationContext.getEnvironment());
}
@Bean
public SpringLiquibase springLiquibase(DataSource dataSource) {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDropFirst(true);
liquibase.setDataSource(dataSource);
liquibase.setChangeLog("classpath:/db/changelog/db.changelog-master.yml");
return liquibase;
}
}
}
测试容器扩展会查找所有带有注释的字段 容器并调用其容器生命周期方法。集装箱 声明为静态字段将在测试方法之间共享。他们 在执行任何测试方法之前仅启动一次 最后一个测试方法执行后停止。集装箱申报 因为每个测试方法都会启动和停止实例字段。
因此,在您的情况下,它将为每个测试方法重新创建一个容器,它只负责启动和停止容器。如果您需要一些测试数据 - 必须手动完成,因为我看到您有 Flyway,那应该可以。
您在谈论什么“上下文问题”?
存储库通常不会单独测试,您可以只测试运行存储库方法的服务,而不是为两者编写测试。如果您无论如何都想测试存储库 - 用
@Before
中的一些数据填充数据库。
如有更多问题请询问。