Search code examples
springspring-data-jpatransactional

How to test rollback scenario in spring


I need to test rollback scenario for this service:

@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);
    }
}

I've created this Integration test case:

@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
    }
}

The question is, why the organization object is still saved in the Database after the rollback happens?

EDIT: Complete source code at github: https://github.com/mhewedy-playground/test-transaction-rollback


Solution

  • The problem was, I was creating the MyService object out of spring context and then expecting the transaction manager to start.

    I've changed the test code to the following and now it works successfully:

    @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());
        }
    }