Search code examples
springspring-bootspring-data-jpaspring-dataspring-transactions

How to test Spring transactions


I'm working to a project with Spring Boot 2.1.0 and I've the following situation.

I've the following repository

@Repository
public interface ActivityRepository extends PagingAndSortingRepository<Activity, Long> {

    @Transactional
    @Modifying
    @Query("") // Imagine a query
    void updateBacklogStatusAge();

    @Transactional
    @Modifying
    @Query("QUERY 2") // Imagine a query
    void updateNextStatusAge();

    @Transactional
    @Modifying
    @Query("QUERY 3") // Imagine a query
    void updateInProgressStatusAge();
}

and the following component

@Component
public class ColumnAgeJob {

    private final ActivityRepository activityRepository;

    public ColumnAgeJob(final ActivityRepository pActivityRepository) {
        activityRepository = pActivityRepository;
    }

    @Transactional
    public void update() {
        activityRepository.updateBacklogStatusAge();
        activityRepository.updateNextStatusAge();
        activityRepository.updateInProgressStatusAge();
    }
}

Now I want to test if the transactional annotation is working.

Basically my goal is to check if a runtimeException raised during the updateInProgressStatusAge() call will cause a rollback of updateNextStatusAge and updateBacklogStatusAge modifications.

How can I do that? Thank you


Solution

  • You can use Mockito in order to change the behaviour of your service or repository by using @SpyBean or @MockBean.

    Unfortunately @SpyBean do not works on JPA repository (https://github.com/spring-projects/spring-boot/issues/7033, this issue is for Spring boot 1.4.1, but I have the same problem with 2.0.3.RELEASE)

    As workaround you can create a test configuration to create manually your mock:

    @Configuration
    public class SpyRepositoryConfiguration {
    
       @Primary
       @Bean
       public ActivityRepository spyActivityRepository(final ActivityRepository real) 
          return Mockito.mock(ActivityRepository.class, AdditionalAnswers.delegatesTo(real));
       }
    }
    

    And in your test:

    @Autowired
    private ActivityRepository activityRepository;
    ....
    @Test
    public void testTransactional() {
        Mockito.doThrow(new ConstraintViolationException(Collections.emptySet())).when(activityRepository).updateInProgressStatusAge();
    
        activityRepository.updateBacklogStatusAge();
        activityRepository.updateNextStatusAge();
        activityRepository.updateInProgressStatusAge();
    
        // verify that rollback happens
    }