Spring 启动@WebIntegrationTest 和TestRestTemplate - 是否可以回滚测试事务?

Spring Boot @WebIntegrationTest and TestRestTemplate - Is it possible to rollback test transactions?

我有一个 Spring 带有 Spring 数据 Rest 的启动应用程序,我在集成测试中使用 @WebIntegrationTestTestRestTemplate。测试的基础 class 看起来像这样:

@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles(profiles = "test")
@SpringApplicationConfiguration(classes = Application.class)
@Transactional
@TransactionConfiguration
@WebIntegrationTest("server.port: 0")
public abstract class IntegrationTest {

   ...

}

我正在通过使用 TestRestTemplate 对资源执行 POST 请求来测试实体的创建。问题是即使我的测试配置为事务性的,将实体保存在数据库中的事务也不会回滚,因此实体在测试后仍保留在数据库中。我有点理解,因为在测试中回滚的事务与持久化实体的事务不同。

现在我的问题是,有什么方法可以回滚测试方法中通过 RestTemplate 发出的请求所触发的事务吗?

is there any way of rolling back the transactions triggered by the requests made through the RestTemplate in a test method?

没有。无法回滚由您部署的应用程序管理的事务。

当您使用 @WebIntegrationTest@SpringApplicationConfiguration 注释您的测试 class 时,Spring Boot 将启动一个嵌入式 Servlet 容器并在其中部署您的应用程序。因此,从这个意义上说,您的测试和应用程序 运行 在两个不同的进程中。

Spring TestContext Framework 仅管理 Test-managed transactions。因此,@Transactional 在您的测试中的存在 class 只会影响本地测试管理的事务,而不影响不同进程中的事务。

正如其他人已经提到的,解决方法是在测试完成后重置数据库的状态。为此,您有多种选择。有关详细信息,请参阅参考手册的 Executing SQL scripts 部分。

此致,

Sam(Spring TestContext Framework 的作者)

您可以使用 MockMvc 而不是 TestRestTemplateRestTemplate。在这种情况下,@Transactional 将回滚数据更改。

如果您正在为 POST 操作测试 @RestController 中的逻辑,那么 MockMvc 就足够了。如果您的测试涉及过滤器,那么您可能需要使用 TestRestTemplate。在这种情况下,正如其他人指出的那样,您需要在测试结束时重置数据库中的测试数据。

我最近需要这个,因为我们的 1300 多个集成测试花费了 +-70 分钟,而且它是可行的,但需要通过一些低级管道。

由于测试用例和启动服务器的内存是共享的,我通过编写一个仅管理 1 个连接的 DataSource 实现来实现它,returns 到 getConnection() 的任何调用者。连接本身是一个连接实现,它充当真实连接的包装器。
然后将数据源添加到@SpringBootTest 配置中。

我不会post这里的代码(公司属性),但这里是主要细节:

DataSource 将连接包装器维护为静态字段。
dataSource.getConnection() 后,所有 "borrowers" 的计数器都会递增。
在 connection.close() 之后,此计数器会递减。如果变为 0,则表示测试用例结束,事务可以回滚。

有 1 个问题需要考虑,那就是 spring 的事务管理:您必须模拟事务回滚。
为此,我在每个 connection.open() 上创建一个保存点 (connection.setSavePoint() ) 并将其推送到 LinkedList 中。对于任何 connection.rollback(),我将其从堆栈中弹出并执行 connection.rollback(savePoint)。

我还不得不破解启用提交功能的可能性,因为数据库必须在 @BeforeAll() 中创建。
我在测试用例中不依赖@Transactional,而是在抽象父类的@Before / @After 方法中调用 dataSource.getConnection / connection.close() 来标记事务边界。

这不是最漂亮的解决方案,但这将测试时间从 70 分钟减少到 30 分钟(同时保持测试绿色 ;)),这是一个不错的胜利。

希望这对某人有所帮助。