I am converting a spring boot application to use webflux by making the endpoints reactive one at a time. Most of the (Spock) tests worked fine, but the ones where I autowire the JPA repository in the test to insert data do not. The repository is always empty when the resource (that we're testing) reads from it. I have tens of tests like this with different repositories and they all have same issue.
Here is an example of a test (the resource we're testing just does findById and returns the example from the repository):
@SpringBootTest
@AutoConfigureWebTestClient(timeout = "60000")
@Transactional
class PaymentControllerIntegrationTest extends Specification {
@Autowired
WebTestClient client
@Autowired
PaymentRepository repo
def "GET /example/{id} returns correct example"() {
given:
def needle = new Example(id: 1L)
def haystack = repo.saveAll([needle, new Example(id: 2L), new Example(id: 3L)])
when:
def response = client.get().uri(EXAMPLE_URL, [id: needle.id.toString()]).exchange()
then:
response.expectStatus().isOk()
response.returnResult(ExampleResponse.class).getResponseBody().blockLast().id == needle.id
}
When I put a breakpoint in the controller and do findAll() the repository is always empty.
- I have tried using TestEntityManager.persistAndFlush instead
- I have tried using repo.saveAllAndFlush instead
- I have tried autowiring ApplicationContext and then building WebTestClient from that, but I never got the autowiring to work
My best guess currently is that the test is setup incorrectly (application context?) so the repository in the test is not the same as the repository in the application.
CodePudding user response:
When using the TestWebClient
(or the TestRestTemplate
) you are actually issuing a real HTTP request to your started server. This request is handled in a different thread and as such uses a new transaction.
Your test is transactional as well but the data hasn't been committed, another transaction can only read committed data (or you need to set the isolation level to READ_UNCOMMITTED
but that is probably not something you should or want to do).
When using MockMvc
you are replacing the actual container with a mocked instance and it uses a MockHttpServletRequest
etc. and executes in the same thread (and thus reuse the same transaction and can see the data).
To solve, make your test not transactional and cleanup the data afterwards. This will however impact the performance of your tests (as committing and then deleting is slower as rolling back a transaction after the test).