Как выполнить интеграционный тест с одной транзакцией для всех вызовов базы данных и откатить его впоследствии?

#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 был выполнен аннотированным методом.