#java #spring-boot #testing #transactions #integration-testing
Вопрос:
Я пишу интеграционный тест, снабженный @SpringBootTest
комментариями .
Предположим, я создаю какой-то продукт, используя чистый SQL из своего теста, вызываю службу вычислений, которая выполняет некоторые вычисления на его основе, и она вызывает другую службу, чтобы сохранить эти результаты вычислений в разных таблицах.
Что мне нужно, так это откатить все изменения, внесенные после завершения теста.
Я читал разные вопросы, пытался использовать @Transactional
на уроке или тестовом методе.
Попытался поместить свойство базы autocommit=false
данных .
Попытался сделать SqlSession
объект и Spy
его @Before
в службу, которая сохраняет данные в разных таблицах и откатывает их @After
. Кроме того, пытался использовать Connection
с autocommit=false
, но это не сработает, все равно это разные транзакции.
@Repository
Бобы также снабжены @Mapper
аннотациями, потому что мы используем My-Batis. Похоже, 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 } }
Ответ №1:
Через некоторое время я нашел решение этой проблемы. Это не смягчено и не блаженно, я бы сказал, что это немного уродливо, но это работает.
У Mybatis SqlSession
есть метод getMapper
, с его помощью вы можете получить картограф, который видит изменения, внесенные в вашу текущую транзакцию, это выглядит так:
DataRepository dataRepository = session.getMapper(DataRepository.class);
Поэтому я собрал все картографы, которые мне нужны, и ввел их в бобы с помощью ReflectionTestUtils.setField
.
SqlSession
сам откат @After
был выполнен аннотированным методом.