spring-bootspring-webfluxpact

Spring boot reactive - webclient - How to handle not found scenario by throw exception or alternate - in Pact


I am following this Pact github tutorial and stuck in one of the scenarios.

Pact links Model 1 : Pact JVM

Model 2 : Junit 5

I am fudging both, but for this test I am inclined to use Model2 ( but using react)

    @Pact(consumer = "ProductCatalogue")
public RequestResponsePact singleProductNotExists(PactDslWithProvider builder) {
    return builder
            .given("product with ID 10 does not exist", "id", 10)
            .uponReceiving("get product with ID 10")
            .path("/product/10")
            .willRespondWith()
            .headers(Map.of("Content-Type", "application/json"))
            .status(404)
            .toPact();
}

@Test
@PactTestFor(pactMethod = "singleProductNotExists", pactVersion = PactSpecVersion.V3)
void testSingleProductNotExists(MockServer mockServer) {
    WebClient webClient = WebClient.builder().baseUrl(mockServer.getUrl()).build();
    productService = new ProductService(webClient);

    try {
        productService.getProductById(10);
        fail("Expected service call to throw an exception");
    } catch (HttpClientErrorException ex) {
        assertThat(ex.getMessage(), containsString("404 Not Found"));
    }
}

Error:

[INFO] 
[ERROR] Failures: 
[ERROR]   ProductServiceClientPactTest.testSingleProductNotExists:190 Expected service call to throw an exception
[INFO] 
[ERROR] Tests run: 4, Failures: 1, Errors: 0, Skipped: 0


[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 1.896 s <<< FAILURE! -- in com.example.ProductServiceClientPactTest
[ERROR] com.example.ProductServiceClientPactTest.testSingleProductNotExists(MockServer) -- Time elapsed: 1.805 s <<< FAILURE!
java.lang.AssertionError: Expected service call to throw an exception
    at com.example.ProductServiceClientPactTest.testSingleProductNotExists(ProductServiceClientPactTest.java:190)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    Suppressed: au.com.dius.pact.consumer.PactMismatchesException: The following requests were not received:
    method: GET
    path: /product/10
    query: {}
    headers: {}
    matchers: MatchingRules(rules={})
    generators: Generators(categories={})
    body: MISSING
        at au.com.dius.pact.consumer.junit.JUnitTestSupport.validateMockServerResult(JUnitTestSupport.kt:110)
        at au.com.dius.pact.consumer.junit5.PactConsumerTestExt.afterTestExecution(PactConsumerTestExt.kt:631)
        ... 2 more

Not sure if I have to follow the same non-reactive approach i.e. expecting exception or do something different.

Goal is to overcome this issue and get pact file created. Please advice


Solution

  • I have to say it is my implementation mistake I had to update service to do the below

    .onStatus(HttpStatusCode::is4xxClientError, response -> Mono.error(new HttpClientErrorException(HttpStatus.NOT_FOUND)))
    

    Full method code

        return webClient.get()
                .uri("/product/{id}", id)
                .retrieve()
                .onStatus(HttpStatusCode::is4xxClientError, response -> Mono.error(new HttpClientErrorException(HttpStatus.NOT_FOUND)))
                .bodyToMono(Product.class);
    

    and update test to use block()

    productService.getProductById(10).block();

    Alternately StepVerifier can be used to verify .expectErrorMatches

    *

    P.S. I did tried onStatus before, but that did not work probably because I did not use block() call in pact test - .onStatus(HttpStatusCode::isError, response -> Mono.error(new Exception(response.toString())))