2

I have been using wiremock for integration testing with spring boot, getting 404 exception.

With message as "Request did not match, as no stubs registered"

I have tried with wiremock server and wiremock rule instance.

Tried with Wiremock's JUnit jupiter extension approach and Application context initializer approach, nothing seems to be work.

package fi.op.bcsio.application.wiremock;

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.configureFor;
import static com.github.tomakehurst.wiremock.client.WireMock.containing;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;

import java.util.function.Supplier;

import static com.github.tomakehurst.wiremock.client.WireMock.*;

import javax.annotation.PostConstruct;

import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.client.RestTemplate;

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.common.ConsoleNotifier;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;

import org.assertj.core.api.Assertions;

@Component
//@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//@ContextConfiguration(initializers = {WireMockInitializer.class})
public class LuukuWiremockStub {

//    @Autowired
//    private WebTestClient webTestClient;
    
    
//  @ClassRule
//  public static WireMockRule wireMockRule = new WireMockRule(WireMockConfiguration.DYNAMIC_PORT);

      @RegisterExtension
      static WireMockExtension wireMockServer = WireMockExtension.newInstance()
        .options(WireMockConfiguration.options().dynamicPort())
        .build();

//    @DynamicPropertySource
//    static void configureProperties(DynamicPropertyRegistry registry) {
//      registry.add("http://op-common.apis.jty.op-palvelut.net/bankingparties/basic/info/v1/parties?context=Pankin ostopolku", wireMockServer::baseUrl);
//    }

//    @AfterEach
//    void resetAll() {
//      // we're using one WireMock server for the test class (see static on the WireMockExtension definition)
//        wireMockRule.resetAll();
//    }

      @Test
      void testGetAllTodosShouldReturnDataFromClient() {
          wireMockServer.stubFor(
          WireMock.get("/parties")
          
          .withHeader("x-request-id", matching("[0-9a-fA-F]{8}\\\\-[0-9a-fA-F]{4}\\\\-[0-9a-fA-F]{4}\\\\-[0-9a-fA-F]{4}\\\\-[0-9a-fA-F]{12}"))
          
            .withHeader("x-session-id", matching("[0-9a-fA-F]{8}\\\\-[0-9a-fA-F]{4}\\\\-[0-9a-fA-F]{4}\\\\-[0-9a-fA-F]{4}\\\\-[0-9a-fA-F]{12}"))
                      
            .withHeader("x-api-key", equalTo("X8Jy6AEXqJjdBx"))
                      
            .withHeader("Authorization", matching("Bearer [a-zA-Z0-9&._-]{1,}"))
            
            .withQueryParam("context", equalTo("Pankin ostopolku"))
          
            .willReturn(aResponse()
            
            .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
              
            .withBody("[{\"userId\": 1,\"id\": 1,\"title\": \"Learn Spring Boot 3.0\", \"completed\": false}," +
                "{\"userId\": 1,\"id\": 2,\"title\": \"Learn WireMock\", \"completed\": true}]"))
        );
        
        // ... controller invocation using the WebTestClient
      }
}

Postman console error: Postman console error Eclipse Console error: Eclipse Console error

xerx593
  • 12,237
  • 5
  • 33
  • 64
  • so for a 404 it is sufficient, that (the url or) *one* of the (`withXXX` *before* , not inside `willReturn(...)`) conditions doesn't match. – xerx593 May 07 '23 at 12:16
  • tips (regarding post): 1. obfuscate "api key"! 2. please prefer "real json" (/error/code/text/curl(!)...) to "image json(/...)" 3. add client/reproduction code. Regarding the issue: start with very basic stub (comment all .withXXX *before* willReturn), successively add/test them back – xerx593 May 07 '23 at 12:23
  • As suggested, I have removed all the checks before will return and just tried to pass the url with get method, but still recieved the same result. Can you please suggest some solution on this. – Shruti Mishra May 08 '23 at 05:05
  • Is there any chance you have another instance of WireMock running in the background (e.g. standalone) and you're accidentally connecting to that one instead of the one you're starting as an extension? – Tom May 08 '23 at 09:58
  • No, Actually wiremock is being running on port 8082, and the other application (which is java spring boot application is being run on port 8080). In 8082, no other instance is running. – Shruti Mishra May 08 '23 at 10:47

3 Answers3

0

Pheeew, that was not that easy one!

  1. https://wiremock.org/docs/spring-boot/ points (currently) "to nirvana" (404)
  2. We want to test "multiple backend" integration, which is complicated per se.

I could update https://rieckpil.de/spring-boot-integration-tests-with-wiremock-and-junit-5/ (which looks very like your question) to latest versions and got it working like so:

  • (spring-boot)started simple (current, maven, java17, webflux+lombok)
  • Main, (fictive) demo application, which calls (get on) "some" ${ext_base_url} REST API, and (tries to) returns its results as a Flux of SomeDto:
    package com.example;
    
    import lombok.EqualsAndHashCode;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.stereotype.Service;
    import org.springframework.web.reactive.function.client.WebClient;
    import reactor.core.publisher.Flux;
    
    @SpringBootApplication
    public class DemoApp {
    
      public static void main(String[] args) {
        SpringApplication.run(DemoApp.class, args);
      }
    
      @Service // 1.
      static class MyService {
    
        private final WebClient webClient;
    
        public MyService(WebClient.Builder webClientBuilder,
            @Value("${ext_base_url}") String baseUrl) { // 2.
          this.webClient = webClientBuilder.baseUrl(baseUrl).build();
        }
    
        public Flux<SomeDto> someRestCall() { // 3.
          return this.webClient.get().uri("/parties")
              .retrieve().bodyToFlux(SomeDto.class);
        }
      }
    
      @lombok.Value(staticConstructor = "of")
      @EqualsAndHashCode(onlyExplicitlyIncluded = true) 
      static class SomeDto { // dto
    
        @EqualsAndHashCode.Include // consider only this, lazy, sorry
        Integer id;
        Integer userId;
        String title;
        boolean completed;
      }
    }
    
  • To mock the "external" API for integration tests we add (current, latest):
    <dependency>
      <groupId>com.github.tomakehurst</groupId>
      <artifactId>wiremock</artifactId>
      <version>3.0.0-beta-8</version> <!-- update! -->
      <scope>test</scope>
    </dependency>
    
  • This ("full" spring boot application) test passes:
    package com.example;
    
    import com.example.DemoApp.MyService;
    import com.example.DemoApp.SomeDto;
    import org.junit.jupiter.api.Test;
    
    import static com.github.tomakehurst.wiremock.client.WireMock.*;
    import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
    import java.io.IOException;
    import org.junit.jupiter.api.AfterEach;
    import org.junit.jupiter.api.extension.RegisterExtension;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.DynamicPropertyRegistry;
    import org.springframework.test.context.DynamicPropertySource;
    import reactor.core.publisher.Flux;
    import reactor.test.StepVerifier;
    
    @SpringBootTest
    class MyWireMockTest {
    
      @RegisterExtension
      static WireMockExtension wm = WireMockExtension.newInstance()
          // dynamic port is the default
          .build();
    
      @DynamicPropertySource
      static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("ext_base_url", wm::baseUrl); // replaces spring property with actual wire mock value
      }
    
      @Autowired
      MyService testee;
    
      @Test
      void test_something_with_wiremock() throws IOException {
        // given:
        wm.stubFor(get("/parties") // static instance!
         // add *successively* "withXXX" conditions here...
         .willReturn(aResponse()
           .withHeader("Content-Type", "application/json")
           .withBody("""
            [
              {"userId": 1,"id": 1,"title": "Learn Spring Boot 3.0", "completed": false},
              {"userId": 1,"id": 2,"title": "Learn WireMock", "completed": true}
            ]
            """)
          )
        );
        // when:
        Flux<SomeDto> response = testee.someRestCall();
        // then:
        StepVerifier.create(response)
            .expectNext(SomeDto.of(1, null, null, false))
            .expectNext(SomeDto.of(2, null, null, false))
            .verifyComplete();
      }
    
      @AfterEach // cleanup (per test method)
      void afterEach() {
          wm.resetAll();
      }
    }
    

Refs:

xerx593
  • 12,237
  • 5
  • 33
  • 64
  • Thanks for providing the solution, as in my requirement, I need to test on the postman not in eclipse ide, thus I can skip the service creation part in it. So here I have 1 ques 1. Can it be worked without using service. (Just by having some enteries for the url in the stub for method). – Shruti Mishra May 09 '23 at 05:40
  • for postman tests, you can run it (without the "test stuff") in a [(psv)"main"](https://wiremock.org/docs/java-usage/), for "serious" usage (e.g. having "dev/test/int/..." instances of the (mocked) external service), i would propose (try the) [docker](https://wiremock.org/docs/docker/) ..it offers a "management ui" (for the endpoint "stubs"), you can (apparently) also persist/port its (docker) "volumes" – xerx593 May 09 '23 at 08:11
  • (these were v2 links, see version notes;) – xerx593 May 09 '23 at 08:14
  • one (big) advantage of the "integrated" solution: you can change the "behavior" (normal/exception/lags...) of the mock without changing the url/additional deployment...quickly – xerx593 May 09 '23 at 08:36
  • you can "hack" also the above (single test!) solution :) just replace "when-then" code with a `Thread.sleep()` (long enough), then knowing/logging wiremock (host&) port, you can: `GET /parties` (kill test from ide/terminal;( – xerx593 May 09 '23 at 08:42
  • so, if you want "to present something" (more than logs/green tests/coverage) to your "stake holders", I would use/try the docker image, these can also be used (as endpoints/"base-url") in your tests (dev/int/uat/...) for "normal scenarios". ... for "exceptional scenarios", you could still run resetable stubs like above. – xerx593 May 09 '23 at 08:56
0

According to comments, here a sample "docker/standalone" solution:

  1. (Optional) Pull the image. I used main-alpine tag, but hope it works also well with the others:

    docker pull wiremock/wiremock:main-alpine
    
  2. (Optional) Create a docker volume (1, 2):

    docker volume create wiremock_data
    
  3. Create "wiremock mappings" (in docker volume or dedicated path). I placed a parties.json file in my ${docker_volumes_location}/wiremock_data[/_data]/mappings:

    {
       "request": {
         "method": "GET",
         "url": "/parties"
       },
       "response": {
         "status": 200,
         "jsonBody": [
             {"userId": 1,"id": 1,"title": "Learn Spring Boot 3.0", "completed": false},
             {"userId": 1,"id": 2,"title": "Learn WireMock", "completed": true}
          ],
          "headers": {
              "Content-Type": "application/json"
          }
       }
    }
    

    ..it should result in /home/wiremock/mappings/parties.json in container's file system, where "parties" is freely chosen/all jsons in this folder get "scanned".

  4. After:

    docker run \
      -it --rm \
      -p 8443:8443 \
      -v wiremock_data:/home/wiremock \
      wiremock/wiremock:main-alpine \
      --https-port 8443 --verbose  
    

    with arguments for:

    • interactive, clean up (-it, --rm)
    • publish port (-p)
    • volume mount (-v)
    • image name/id/tag
    • container arguments (https-port, verbose)
  5. ..and successful container start, we can "browse" https://localhost:8443/parties:

    • with curl:
      >curl --insecure 'https://localhost:8443/parties'           
      [{"userId":1,"id":1,"title":"Learn Spring Boot 3.0","completed":false}, 
      {"userId":1,"id":2,"title":"Learn WireMock","completed":true}]
      
    • , postman: postman screenshot
    • and browser: browser screenshot

    ... after disabling (self-signed) SSL validation.

  6. (https://localhost:8443/__admin/ shows a (JSON) overview of all current mappings.)

  7. (For mapping modifications, we have to restart the container.)

Details and References:

To run (shown) spring boot test against docker (wiremock)

We:

  1. Run the docker instance as described (1. - 7.), on https://localhost:8443.
  2. Remove "wiremock" dependency(/ies) from (spring-boot) pom.xml.
  3. (Without changing the application,) Adjust test to:
    package com.example;
    
    import javax.net.ssl.SSLException;
    
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.boot.test.context.TestConfiguration;
    import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
    import org.springframework.context.annotation.Bean;
    import org.springframework.http.client.reactive.ReactorClientHttpConnector;
    
    import com.example.DemoApp.MyService;
    import com.example.DemoApp.SomeDto;
    
    import io.netty.handler.ssl.SslContext;
    import io.netty.handler.ssl.SslContextBuilder;
    import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
    import reactor.core.publisher.Flux;
    import reactor.netty.http.client.HttpClient;
    import reactor.test.StepVerifier;
    
    @SpringBootTest(
      /* this sets base url for the client: */
      properties = "ext_base_url=https://localhost:8443"
    )
    class MyWireMockTest {
    
      // RE: How to disable SSL validation for springboot-webclient test? Voila:
      @TestConfiguration // 1. (test configuration!) "Adds to" current spring context (doesn't "replace")
      static class MyInsecureWebClientConfig { // should be static!
       @Bean // WC customizer bean! (spring-boot, babe)
       WebClientCustomizer wcCustomizr() throws SSLException {
         // combining with [6]:
         SslContext context = SslContextBuilder.forClient()
            .trustManager(InsecureTrustManagerFactory.INSTANCE)
            .build();
         return wcb -> {
          wcb.clientConnector(new ReactorClientHttpConnector(
             HttpClient.create().secure(t -> t.sslContext(context))));
         };
       }
      }
    
      @Autowired
      MyService testee;
    
      @Test
      void test_something_with_wiremock() throws InterruptedException {
       // given: config, wiremock instance...
       // when:
       Flux<SomeDto> response = testee.someRestCall();
       // then:
       StepVerifier.create(response)
          .expectNext(SomeDto.of(1, null, null, false))
          .expectNext(SomeDto.of(2, null, null, false))
          .verifyComplete();
      }
    
    }
    
    [6]
xerx593
  • 12,237
  • 5
  • 33
  • 64
0

After updating my dependancy to

<dependency>
  <groupId>com.github.tomakehurst</groupId>
  <artifactId>wiremock</artifactId>
  <version>3.0.0-beta-8</version> <!-- update! -->
  <scope>test</scope>
</dependency>

It worked for me