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?