我正在寻找一种方法来保持我的组件测试自包含。因此,为了实现这种行为,在一些测试中,我需要有一个"干净的数据库"或至少一个"干净的表"。我仍然找不到在测试容器内做到这一点的方法。以下是我到目前为止所做的尝试:容器设置类:
PostgreSqlTestContainer实现QuarkusTestResourceLifecycleManager {
public static final PostgreSQLContainer<?> POSTGRES = new PostgreSQLContainer<>("postgres:alpine");
@Override
public Map<String, String> start() {
POSTGRES.start();
return some_db_config_as_per_doc;
}
@Override
public void stop() {
POSTGRES.stop();
}
下面是测试类:
@QuarkusTest
@QuarkusTestResource(PostgreSqlTestContainer.class)
class UserResourcesTest {
@Test
scenario_one(){
// create a new user
// do some stuff (@POST.. check HTTP == 201)
}
@Test
scenario_two(){
// create new user
// do some stuff (@POST.. check HTTP == 201) (pass)
// look for all users on database
// do more stuff (@GET.. check HTTP == 200) (pass)
// assert that only 1 user was found
// since scenario_one should not interfere with scenario_two (fail)
}
}
第二个场景失败了,因为第一个测试中的一些"脏"仍然在db容器上。我试图停止/启动每个测试的容器。(非常非常慢的过程,有时我得到一个错误,非常慢)。
@BeforeEach
void setup(){
PostgreSqlTestContainer.POSTGRES.stop();
PostgreSqlTestContainer.POSTGRES.start();
}
也试图截断表/删除整个db:
@Inject
EntityManager entityManager;
@BeforeEach
private void rollBack(){
truncate();
}
void truncate(){
Query nativeQuery = entityManager.createNativeQuery("DROP DATABASE IF EXISTS db_name");
nativeQuery.executeUpdate();
}
我正在寻找任何解决这个问题的方法,我只是想在每次测试之前以某种方式使用@BeforeEach
来清理DB。我的意思是,我只想要一个干净的环境来进行每次测试。
假设您正在使用Panache和Active Record模式,您可以这样做:
@BeforeEach
@Transactional
public void cleanUp() {
MyEntity.deleteAll();
}
如果您正在使用存储库,那么只需注入存储库并调用deleteAll()方法。
还有@TestTransactional可用于单元测试。这将在测试完成后回滚对db的任何更改。
顺便说一句,我是用手机打字的,所以很抱歉有任何语法错误。
创建名称为test_template
的模板测试数据库。
每次测试后,
从测试数据库断开所有会话(PostgreSQL v13不需要):
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'test';
drop database with
DROP DATABASE test;
在v13中,使用额外的
FORCE
选项。用
创建新的测试数据库CREATE DATABASE test TEMPLATE test_template;
注意:您必须在JDBC驱动程序中启用自动提交才能使CREATE DATABASE
和DROP DATABASE
工作。
你可以试试:
从容器定义中删除static子句。它将强制它在每次start()执行时创建容器。
PostgreSQLContainer
你也可以删除@beforeach中的stop,我认为这是不必要的
您可以在启动提供模式SQL的容器时创建表:
PostgreSQLContainer<?> postgresContainer = new PostgreSQLContainer< ("postgres:14-alpine") .withDatabaseName("db") .withUsername("username") .withPassword("password") .withCopyFileToContainer( MountableFile.forClasspathResource("/schema.sql"), "/docker-entrypoint-initdb.d/" );
如果你想根据你的测试方法插入一些数据你可以这样做并在测试开始时调用它:
private void loadData(String scriptPath) { try { postgresContainer.copyFileToContainer( MountableFile.forClasspathResource(scriptPath), "/data/import.sql" ); postgresContainer.execInContainer( "psql", "-U", postgresContainer.getUsername(), "-d", postgresContainer.getDatabaseName(), "-f", "/data/import.sql" ); } catch (IOException e) { throw new RuntimeException(e); } catch (InterruptedException e) { throw new RuntimeException(e); } }
丢弃测试期间所做的更改
截断表:只需截断所有表,而不是删除整个数据库。它也更快,对你来说可能是最好的选择。
代码例子:
// Code Example on how to truncate tables
@BeforeEach
void cleanDatabase() {
entityManager.createNativeQuery("TRUNCATE TABLE user_table CASCADE").executeUpdate();
// Here you can add more tables if you want to
}
事务回滚:
我想确保在测试期间所做的更改被丢弃的最常见的选择是这个。您基本上启动一个事务和测试的最开始,并在它完成时回滚它。
代码例子:
// Example Code of a Transaction Rollback
@Inject
UserTransaction userTransaction;
@BeforeEach
void beginTransaction() throws Exception {
userTransaction.begin();
}
@AfterEach
void rollbackTransaction() throws Exception {
userTransaction.rollback();
}
使用这种方法,您也比删除和重新创建整个数据库或表更快。然而,只有当你的测试是事务安全的,并且你不测试非事务特性时,这才会起作用。
liquid base or Flyway:
这只适用于使用迁移工具,如Liquibase或Flyway。您基本上可以在每次测试之前将数据库重置为特定的迁移或基线。
代码例子:
// Code Example with Flyway
@Inject
Flyway flyway
@BeforeEach
void resetDatabase(){
// This method cleans the database.
flyway.clean();
// This method applies all migrations from the start.
flyway.migrate();
}
如果你有任何问题,请提出来。