问题描述
我正在尝试使用 Quarkus 学习一些新东西,但我有点卡在了某些事情上:
我想创建一些自我维持的测试,我的意思是,我应该能够独立运行每个测试。
我正在使用 Testcontainers(PostgreSQL) 来运行一些组件测试,但我希望每个 @Test
都能够在“干净的数据库”上运行,但我不认为停止容器并为每个 @Test
再次启动它是个好主意。我可以有一个 @BeforeEach(container.dropColumn()) 或 @BeforeEach(container.truncateColumn()) 但是:1 - 我不知道这是否是更好的方法,2 - 我不知道如何做到这一点.
给定一个假设的测试场景:
场景 1 - 通过
- 注册新用户
- 通过 id 查找用户
- 断言(one_user_on_db)
场景 2 - 失败
- 尝试使用无效数据注册新用户
- 查找所有用户
- 断言(zero_users_on_db)
第二个测试失败,因为第一个场景中的数据仍在数据库中。 在这里,一些代码可以提供帮助。
资源
@Inject
UserService userService;
@POST
@Transactional
public Response saveUser(@Valid UserDto user){
return Response.status(Response.Status.CREATED)
.entity(userService.saveUser(user,user.getPassword())).build();
}
@GET
public Iterable<User> findAll(){
return userService.findAll();
}
测试
@Test
void shouldSuccessfullyCreateUser(){
User mockUser = MockUser.onlyMandatoryFields() //MockUser
given()
.body(mockUser)
.contentType(ContentType.JSON)
.when()
.post("/users").prettyPeek()
.then()
.statusCode(Response.Status.CREATED.getStatusCode());
@Test
void shouldHaveAnEmptyDatabase(){
User[] userList = given()
.contentType(ContentType.JSON)
.when()
.get("/users").prettyPeek()
.then()
.extract()
.response()
.as(User[].class);
assertEquals(0,userList.length);
}
我已经按照 Quarkus Docs 中的描述尝试过 @TestTransaction,但没有成功。
我正在寻找来自 Spring 的 @DirtiesContext
之类的东西。
无论如何,如果您想进一步查看代码,我有一个开放的 repository。 可以在 here 中找到测试。
解决方法
使用 Quarkus @QuarkusTestProfile 可以实现与 Spring DirtiestContext 类似的效果,描述为:
如果测试的配置文件与之前运行的测试不同,则 Quarkus 将在之前关闭并使用新配置文件启动 运行测试。这显然有点慢,因为它增加了 关闭/启动周期到测试时间但给出了大量的 灵活性。 我认为它可能对你有用,但会很慢。
考虑其他解决方案
- 编写一个由@TestTransaction 注释的集成测试来验证服务类,并编写一个测试来验证您的 REST 控制器并注入服务的模拟
- 在每次测试前截断数据库,
- 创建一个 REST 调用,该调用将删除创建的用户并从您的测试中调用它,
@TestTransaction
在您直接测试存储库或 CDI bean 时正常工作。
我已经按照 Quarkus Docs 中的描述尝试了 @TestTransaction
,但没有成功。
如果测试在与测试代码相同的事务上下文中运行,它就可以工作。
您的 REST 资源在与您的测试方法不同的事务上下文中工作;因此, @TestTransaction
在您的情况下不起作用。在您的情况下,事务在 rest 调用结束时提交;因此你不能回滚它。
查看直接验证存储库的工作测试示例。
@Test
@TestTransaction
void shouldFail_whenCreatingNewLedgerWithUnrecognizedType() {
//when/then
assertThatThrownBy(() -> customSqlRepository.insertWithUnrecognizedType())
.isInstanceOf(PersistenceException.class)
.hasMessageContaining("Check constraint violation")
.hasMessageContaining("LEDGER_ACCOUNT_TYPE IN(");
}