有没有办法在 Quarkus 单元测试中回滚事务?

Posted

技术标签:

【中文标题】有没有办法在 Quarkus 单元测试中回滚事务?【英文标题】:Is there a way to rollback a Transaction in Quarkus Unit Tests? 【发布时间】:2021-04-30 21:18:05 【问题描述】:

我正在尝试使用 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找到。

【问题讨论】:

@TestTransaction 似乎很脆弱 - 例如,它不会回滚序列。我收到Duplicate entry '4-2' for key 'PRIMARY' 之类的错误 【参考方案1】:

@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(");

【讨论】:

【参考方案2】:

使用 Quarkus @QuarkusTestProfile 可以实现与 Spring DirtiestContext 类似的效果,描述为:

如果测试的配置文件与之前运行的测试不同,则 Quarkus 将在之前关闭并使用新配置文件启动 运行测试。这显然有点慢,因为它增加了一个 关闭/启动周期到测试时间,但提供了大量 灵活性。 我认为它可能对你有用,但会很慢。

考虑其他解决方案

    编写一个由 @TestTransaction 注释的集成测试来验证服务类,并编写一个测试来验证您的 REST 控制器并注入服务的模拟 在每次测试前截断数据库, 创建一个 REST 调用,该调用将删除创建的用户并从您的测试中调用它,

【讨论】:

以上是关于有没有办法在 Quarkus 单元测试中回滚事务?的主要内容,如果未能解决你的问题,请参考以下文章

事务无法在 codeigniter 中回滚

如何在 SQL Server 中回滚或提交事务

事务似乎已回滚,但它们并未在数据库中回滚

PostgreSQL:在 plpgsql 函数中回滚事务?

如何在 Amazon S3 存储桶中回滚到以前的版本?

Spring - 事务应该在一种方法中提交,但应该在执行数据库事务的其他方法中回滚