如何对所有数据库调用进行一个事务的集成测试并在之后回滚?

How to make an integration test with one transaction for all database calls and rollback it afterwards?

我正在编写一个集成测试,用 @SpringBootTest 注释。

假设我使用我的测试中的纯 SQL 创建一些产品,调用计算服务基于它进行一些计算,然后它调用另一个服务将这些计算结果保存在不同的表中。

我需要在测试完成后回滚所有所做的更改

我阅读了不同的问题,尝试在 class 或测试方法上使用 @Transactional

试图将数据库 属性 autocommit=false.

试图将 SqlSession 对象和 Spy 它在 @Before 中制作成将数据保存到不同表中并在 @After 中回滚的服务。此外,尝试将 Connectionautocommit=false 一起使用,但它不起作用,它是 所有不同的交易 无论如何。

因为我们用的是My-Batis,所以@Repository个bean也注解了@Mapper。似乎 my-batis 在调用映射器时创建新事务。

所以我剩下的唯一想法就是为集成测试启动内存数据库并使用它。 或者我可能遗漏了一些东西,可以通过交易管理以其他方式完成?

如何在一个事务中执行所有调用,以便他们看到所做的更改并在之后回滚?

这是我尝试做的测试的示例代码:

@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class MyTestClass {

    @Autowired
    private DataRepository dataRepository;
    @Autowired
    private CalculateService calculateService;

    @Before
    public void setUp() throws SQLException {
        SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
        connection = session.getConnection();
        connection.setAutoCommit(false);
        spySession = Mockito.spy(session);
        // other stuff with  SqlSessionFactory mocking and injecting the mock 
       }
    
    @After
    public void cleanUp() {
        spySession.rollback();
    }

    @Test
    public void myTest(){
        ResultSet resultSet = connection.prepareStatement("Insert into...").executeQuery(); // create new product
        resultSet.next();
        String createdProductId = resultSet.getString(1);

        Product createdProduct = dataRepository.getProduct(createdProductId);
        assertNotNull(createdProduct); // fails here

        calculateService.calculate(createdProduct); // this call creates new threads there
        // and calls other service that saves data in different trasaction aswell

        createdProduct = dataRepository.getProduct(createdProductId); // call again to assert calculation results
        
        assertSomeField(createdProduct.getSomeField);
        // other assertions here
    }

 
}   

一段时间后,我找到了解决这个问题的方法。不精致也不漂亮,有点难看,但还行。

Mybatis 的 SqlSession 有一个方法 getMapper,使用它你可以得到一个映射器,它可以看到你当前事务中所做的更改,它看起来像这样:

DataRepository dataRepository = session.getMapper(DataRepository.class);

所以我得到了我需要的所有映射器并使用 ReflectionTestUtils.setField.

将它们注入到 bean 中

SqlSession 本身在 @After 注释方法中被回滚。