Мне нужно протестировать сценарий отката для этой службы:
@Service
public class MyService2 {
private final Entity2Repo entity2Repo;
public MyService2(Entity2Repo entity2Repo) {
this.entity2Repo = entity2Repo;
}
public void create(Long entity1Id) {
Entity2 entity2 = new Entity2();
entity2.assignToEntity1(entity1Id);
entity2Repo.save(entity2);
}
}
Я создал этот тестовый пример интеграции:
@Transactional
@RunWith(SpringRunner.class)
@SpringBootTest(classes = JpaApplication.class)
public class MyServiceIntTest {
@Autowired
private Entity1Repo entity1Repo;
@Autowired
private EntityManager entityManager;
@Test
public void create() throws Exception {
MyService2 mock = mock(MyService2.class);
doThrow(new RuntimeException("bla bla")).when(mock).create(anyLong());
MyService myService = new MyService(entity1Repo, mock);
try {
myService.create("some name");
fail("should never fails");
} catch (RuntimeException e) {
assertThat(e.getMessage()).isEqualTo("bla bla"); // success
}
Entity1 dbEntity1 = (Entity1) entityManager.createQuery("from Entity1 where name = 'some name'").getSingleResult();
assertThat(dbEntity1).isNull(); // fail, however it should be null as it is rolled-back
}
}
Возникает вопрос, почему объект организации все еще сохраняется в базе данных после отката?
РЕДАКТИРОВАТЬ: Полный исходный код на github: https://github.com/mhewedy-playground/test-transaction-rollback
@hovanessyan то же самое, я считаю, что Spring boot добавляет PlatformTransactionManager на основе типа источника данных (который был бы в моем случае JpaTransactionManager) (я просто печатаю в начале метода тестирования: org.springframework.orm.jpa.JpaTransactionManager@6e03db1f )
В приведенном выше коде есть проблема, экземпляр MyService, созданный в тесте, не управляется Spring, поэтому при вызове метода create транзакция не создается.
Проблема заключалась в том, что я создавал объект MyService вне контекста Spring, а затем ожидал запуска диспетчера транзакций.
Я изменил тестовый код на следующий, и теперь он успешно работает:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = JpaApplication.class)
public class MyServiceIntTest {
@Autowired
private EntityManager entityManager;
@Autowired
private MyService myService;
@MockBean
private MyService2 myService2;
@Transactional
@Test(expected = RuntimeException.class)
public void createWillRollback() throws Exception {
doThrow(new RuntimeException("bla bla")).when(myService2).create(anyLong());
myService.create("some name");
assertThat(count()).isEqualTo(0);
}
@Transactional
@Test
public void create() throws Exception {
doNothing().when(myService2).create(anyLong());
myService.create("some name");
assertThat(count()).isEqualTo(1);
}
private long count() {
return ((Long) entityManager.createQuery("select count(*) from Entity1").getSingleResult());
}
}
Эта строка: assertThat(count()).isEqualTo(0); никогда не достигается !!
Можете ли вы быстро попытаться расширить свой тест AbstractTransactionalJUnit4SpringContextTests? Также есть ли у вас bean-компонент PlatformTransactionManager в ApplicationContext, который загружается с помощью семантики @ContextConfiguration, как описано в документации?