如何对所有数据库调用进行一个事务的集成测试并在之后回滚?
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
中回滚的服务。此外,尝试将 Connection
与 autocommit=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
注释方法中被回滚。
我正在编写一个集成测试,用 @SpringBootTest
注释。
假设我使用我的测试中的纯 SQL 创建一些产品,调用计算服务基于它进行一些计算,然后它调用另一个服务将这些计算结果保存在不同的表中。
我需要在测试完成后回滚所有所做的更改。
我阅读了不同的问题,尝试在 class 或测试方法上使用 @Transactional
。
试图将数据库 属性 autocommit=false
.
试图将 SqlSession
对象和 Spy
它在 @Before
中制作成将数据保存到不同表中并在 @After
中回滚的服务。此外,尝试将 Connection
与 autocommit=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
.
SqlSession
本身在 @After
注释方法中被回滚。