1

Notes on the solution:

@Transactional annotation and using lazy loading repository access seems to have been the root cause.

Context:

I'm writing Junit5 integration tests for a SpringBoot application (v2.2.5.RELEASE). It would start up SpringBoot with a webEnvironment, call each rest endpoint and assert the results.

Since we have a cache layer between our service and persistence layers I also injected the repository to directly check the database contents as well.

Problem:

It seems that I can access the database directly through the injected repository or through the web call that eventually calls the same repository (not sure if it calls the same instance though, with all the SpringBootTest magic going on in the background).

When I try to call both in the same test case whichever comes first runs properly and the second fails spectacularly.

Case A: Web endpoint access followed by a direct repository access

  • Web endpoint uses the database, creates the resource (confirmed by a separate db query)
  • Direct repository access does not find any entity even though its clearly in the db

Case B: Direct repository access followed by a web endpoint access

  • Direct repository access creates the resource (confirmed by a separate db query)
  • Web endpoint does not find any entity even though its clearly in the db
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@ActiveProfiles("it")
@EnableAutoConfiguration(exclude = SecurityAutoConfiguration.class)
@Sql(scripts = {"classpath:schema_init.sql"})
@Transactional
public class ClientIntegrationTest {

    // This is the same repository class that is at the end of the api call
    @Autowired
    private ClientRepository repository;


    // CASE A

    @Test
    @DisplayName("Create a new client")
    @Sql(scripts = "classpath:data_int.sql")
    void createNewClientTest() {
        // given
        Client rawClient = new Client("testdetails");

        // when
        Client savedClient =  Unirest
                .post("myendpoint_save")
                .body(rawClient)
                .asObject(Client.class)
                .getBody();

        // then
        assertThat(repository.findById(savedClient.getId()).get())
                .as("The DB state is the same as the returned state")
                .isEqualTo(savedClient);
    }


    // CASE B

    @Test
    @DisplayName("Get client by id")
    @Sql(scripts = "classpath:data_int.sql")
    void getClientByIdTest() {
        // given
        Client savedEntity = repository.save(new Client("testdetails"));

        // when
        Client retrievedClient = Unirest
                .post("myendpoint_getbyid")
                .queryString("clientId", savedEntity.getId())
                .asObject(Client.class)
                .getBody();

        // then
        // omitted assertions
    }

}

I assume that the source of the problem might be related to the webEnvironment running in a separate thread, but even that makes no sense as the database is the single source of truth in both operations.

So why does the same repository have different results when looking at the same db state within the same test case directly and through a web call?

Péter Veres
  • 955
  • 1
  • 10
  • 25
  • 1
    Another idea: @Transactional only works for direct repository access since transactions are usually Thread-bound. Have you tried wi5hout @Transactional? – johanneslink May 19 '20 at 19:30
  • @johanneslink thanks, that might be a step in the right direction as it has solved part of the problem and revealed new details that might help to find the root cause. I'll update the desciption – Péter Veres May 20 '20 at 07:19
  • Just for the record there were two other things that led me believe that removing Transactional would not solve the problem: - I was missing a manual cache reset step after adding an entity to the DB (silly me) - In another test case instead of findById() that works with eager loading I was using getOne() that is lazy loading and I got a session not found error. @johanneslink could you create an answer, so that I can mark it as the solution? thanks again! – Péter Veres May 20 '20 at 08:01

1 Answers1

1

Removing @Transactional could solve your problem.

@Transactional is meant to control transactional behaviour in direct repository access only.

johanneslink
  • 4,877
  • 1
  • 20
  • 37