0

I am using JUnit 5 with Spring boot 2.2.4 for integration testing my Spring Boot application code with MySQL 5.7 database but the changes that a previous test method makes to database aren't rolled back causing errors in the ones that follow.

I've tried answers from different stackoverflow posts such as adding @Transactional (from Spring), @Rollback (method as well as class level), @TestExecutionListeners(listeners = {TransactionalTestExecutionListener.class}). I've tested these options individually (as well as in combination) but adding them in the test code together.

This is my test code:

@DataJpaTest
@Transactional
@Rollback
@ExtendWith(SpringExtension.class)
@Import({UserUsecasesImpl.class})
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class UserUsecasesImplINTTest {

    @Autowired
    private TestEntityManager entityManager;
    @Autowired
    @Spy
    private UserRepository userRepository;
    @Autowired
    private UserUsecases userUsecases;

    @Test
    void injectedComponentsAreNotNull(){
        assertNotNull(entityManager);
        assertNotNull(userRepository);
        assertNotNull(userUsecases);
    }

    @Test
    @Rollback
    void Create_EntityAlreadyExists_ReturnsException() {
        // set up
        User expected = UserFixture.user1.toBuilder().build();
        // entityManager.persistAndFlush(expected);
        userRepository.saveAndFlush(expected);

        // execute
        User actual = userUsecases.create(expected);

        // verify
        assertEquals(expected, actual);
        verify(userRepository).findOne(any(Example.class));
        verify(userRepository, never()).save(any(User.class));
        verifyNoMoreInteractions();
    }

    @Test
    void Find_WhenUserPresent_ReturnUser() {
        // // set up
        User expected = UserFixture.user1.toBuilder().build();
        entityManager.persistAndFlush(expected);

        // execute
        User actual = userRepository.find(expected);

        // verify
        assertEquals(expected, actual);
    }

    @Test
    // @Transactional
    void Find_WhenUserNotPresent_ReturnNull() {
        // // set up
        User expected = UserFixture.user1.toBuilder().build();
        // entityManager.persistAndFlush(UserFixture.user2.toBuilder().build());

        // execute
        User actual = userUsecases.find(expected);

        // verify
        assertNull(actual);
    }
}

UserUsecasesImpl is the class that I am trying to test which contains usecases such as creating a user, finding a user, get all users, etc. There is no special configuration anywhere else in the code.

Omkar Manjrekar
  • 103
  • 1
  • 11
  • ok, PLEASE go and rename all the classes to normal naming conventions I can't read this. Let Repository called class implement the JPA repository, don't call POJOs 'DAO' and Datasource?? I'm getting OCD just reading this. – J Asgarov Aug 08 '20 at 19:08
  • Alright. Sorry for the inconvenience caused. – Omkar Manjrekar Aug 08 '20 at 19:13
  • The class names make no sense as @JAsgarov mentioned. Please change this to proper naming or else it is just unreadable. Second thing, why do an integration test with your actual DB. Use an h2 in the classpath, scoped as a test, or testRuntimeOnly. Define a proper application-test.yml in your resources. Declare the test property source and use that. I see many annotations used for no use, as DataJpaTest itself is a meta-annotation of most of the annotations you using. – Priyak Dey Aug 08 '20 at 19:24
  • I have updated the class names. @Priyak Dey Initially I was using h2 but I wanted to make sure of what was happening in the DB and the easiest way I could think of was using MySQL so that I can connect to it and see whats happening at the breakpoint. I have defined an additional application-test.properties activated by spring.profiles.active="test". – Omkar Manjrekar Aug 08 '20 at 19:28
  • This is not a solution but I was able to get the test cases work for me. I switched from MySQL to H2 and then it does rollback after each test method. – Omkar Manjrekar Aug 20 '20 at 07:44

1 Answers1

0

Please implement following fixes and let me know if problem still there:

  1. Remove all @Transactional and @Rollback. Keep @DataJpaTest which by default makes all of your tests @Transactional and therefore by default will roll them all back.
  2. don't use saveAndFlush. The flush part is flushing save operation into the database and most likely causes your issue. Use save instead. More here https://www.baeldung.com/spring-data-jpa-save-saveandflush
  3. If you are expecting exception assert it with assertThrows.

In general try not to throw every single annotation you know into the mix at the same time. Add them one at a time as you need them, try to understand what they do.

What is UserUseCases and why does it do most of the logic I would expect the Repository class to do?

J Asgarov
  • 2,526
  • 1
  • 8
  • 18
  • I tried the suggestions you recommended, especially 2nd but doesn't resolve the issue (flush doesnt mean commit https://stackoverflow.com/questions/14581865/hibernate-flush-and-commit). Doesn't Spring Data (or hibernate) rely on native transcations of database cause if that's the case it should be able to rollback even though it has pushed data to db. I actually moved to Spring from Flutter where I would follow this structure https://resocoder.com/2019/08/27/flutter-tdd-clean-architecture-course-1-explanation-project-structure/. TL;DR usecases may interact with more than 1 repository. – Omkar Manjrekar Aug 08 '20 at 20:27