Wirmock, Webclient - rlip/java GitHub Wiki

Webclient

Lepiej użyć webkienta niż resttemplate, tak zalecają twórcy. RestClient nie jest już rozwijany

    public List<Movie> retrieveAllMovies(){
        String getAllMoviesUrl=GET_ALL_MOVIES_V1;
        List<Movie> movieList;
        try{
        movieList=webClient.get().uri(getAllMoviesUrl)
        .retrieve() // actual call is made to the api
        .bodyToFlux(Movie.class) //body is converted to flux(Represents multiple items)
        // mono by było jak spodziewamy się 1 wpisu
        .collectList() // collecting the httpResponse as a list\
        .block(); // This call makes the Webclient to behave as a synchronous client.

Wiremock

//wiremock
    testImplementation"com.github.tomakehurst:wiremock-jre8-standalone:2.24.1"
            implementation"com.github.JensPiegsa:wiremock-extension:0.4.0"

            -----

@ExtendWith(WirMockExtention.class)
public class MoviesRestClientTest {

    @InjectServer
    WireMockServer wireMockServer;

    @ConfigureWiremock
    Options options = wiremockConfig()
            .port(8088)
            .notifier(new ConsoleNotifier(true)); //true - dodatkowe informacje w konsoli

    @BeforeEach
    void setUp() {
        int port = wireMockServer.port();
        String baseUrl = String.format("http://localhost:%s/, port");
        webCient = WebClient.create(baseUrl);
        movieRestClient = new MoviesRestClient(webclient);
    }

    @Test
    void retrieveAllMovies() {
        //given
        stubFor(
                get(anyUrl())
                .willReturn(
                        WireMock.aResponse()
                                .withStatus(HttpStatus.OK.value())
                                .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                                .withBodyFile("all-movies.json") //to zdaje się że domyślnie ma być w resources/__files
                )
        );
        //when
        List<Movie>x
    }
}

Stubs

stubFor(tuGetAnboPostAlboInne(tuPoniższe))

  • anyUrl()
  • urlPathEqualTo(dokladnyUrl)
  • urlPathMatching("movieservice/v1/movie/[0-9]+") - tu można regex

QueryParams

Albo tak albo użyć ?nazwa=wartosc i to jest rekomendowane, bo to niżj też przejdzie jak zapomnimy withQueryParams

String movieName = "Avengers";
stubFor(get(urlPathEqualTo(MOVIE_BY_NAME_QUERY_PARAM_V1))
        .withQueryParam("movie_name", equalTo(movieName))  //są też inne niż equalTo
        .willReturn(WireMock.aResponse()
        .withStatus(HttpStatus.OK.value())
        .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
        .withBodyFile("avengers.json")));

RequestBody

żeby sprawdzić body z np. posta. Więcej przykłdów: Więcej przykładów: https://wiremock.org/docs/request-matching

       stubFor(post(urlPathEqualTo(ADD_MOVIE_V1))
                .withRequestBody(matchingJsonPath(("$.name"))) //żeby był taki parametr
                .withRequestBody(matchingJsonPath(("$.name"), equalTo("Toys Story 4"))) //albo można sprawdzić dokładnie
                .withRequestBody(matchingJsonPath(("$.cast"), containing("Tom"))) //albo żeby chociaż zawierał
                .willReturn(WireMock.aResponse()
                        .withStatus(HttpStatus.CREATED.value())
                        .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                        .withBodyFile("add-movie.json")));

Selective Proxy

Jeśli chce się żeby jak coś nie jest zasubowane to udeżało na rzeczywiste endpointy, to można zrobić:

    @Before
    public void setUp() {
        ...
        stubFor(any(anyUrl()).willReturn(aResponse().proxiedFrom("http://localhost:8081")));
    }

Response templating

Trzeba dodać rozszerzenie. Więcej przykładów: https://wiremock.org/docs/response-templating

    @ConfigureWiremock
    Options options = wiremockConfig()
            .port(8088)
            .notifier(new ConsoleNotifier(true))
            .extensions(new ResponseTemplateTransformer(true));

Teraz można używać:

//movie-template.json
{ 
    "movie_id": "{{request.path.[3]}}", // 3 - to 4 pozycja pomiędzy "/" w adresie
    "name": "Batman Begins",
    "year": 2005,
    "cast": "Christian Bale, Katie Holmes , Liam Neeson",
    "release_date": "2005-06-15"
}
//albo
{
    "timestamp": "{{now}}",
    "status": 404,
    "error": "Not Found",
    "message": "No Movie Available with the given Id - {{request.path.[3]}}",
    "path": "{{request.path}}"
}
//albo jak szukamy
{
    "movie_id": 4,
    "name": "The {{request.query.movie_name}}",
    "year": 2012,
    "cast": "Robert Downey Jr, Chris Evans , Chris HemsWorth",
    "release_date": "2012-05-04"
}

Dynamic Response Stub, random data

Więcej przykładów generowania losowych wartości na https://wiremock.org/docs/response-templating

{
  "movie_id": "{{randomValue length=2 type='NUMERIC'}}",
  "name": "{{jsonPath request.body '$.name'}}",
  "year": "{{jsonPath request.body '$.year'}}",
  "cast": "{{jsonPath request.body '$.cast'}}",
  "release_date": "{{date parseDate(jsonPath request.body '$.release_date')}}"
}

Verify

    @Test
    public void deleteMovieByName() {

        //given
        stubFor(post(urlPathEqualTo(ADD_MOVIE_V1))
                .withRequestBody(matchingJsonPath("$.name", equalTo("Toy Story 5")))
                .willReturn(WireMock.aResponse()
                        .withStatus(HttpStatus.CREATED.value())
                        .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                        .withBodyFile("add-movie.json")));

        stubFor(delete(urlPathMatching("/movieservice/v1/movieName/.*"))
                .willReturn(WireMock.ok()));

        String toyStoryCrew = "Tom Hanks, Tim Allen";
        Movie toyStory = new Movie(null, "Toy Story 5", 2019, toyStoryCrew, LocalDate.of(2019, 06, 20));
        Movie movie = moviesRestClient.addNewMovie(toyStory);

        //when
        String responseMessage = moviesRestClient.deleteMovieByName(movie.getName());

        //then
        assertEquals("Movie Deleted SuccessFully", responseMessage);

        verify(postRequestedFor(urlPathEqualTo(ADD_MOVIE_V1))
                .withRequestBody(matchingJsonPath("$.name", equalTo("Toy Story 5"))));
        verify(deleteRequestedFor((urlPathMatching("/movieservice/v1/movieName/.*"))));
        verify(exactly(1), postRequestedFor(urlPathEqualTo(ADD_MOVIE_V1))
                .withRequestBody(matchingJsonPath("$.name", equalTo("Toy Story 5"))));
        verify(exactly(1), deleteRequestedFor((urlPathMatching("/movieservice/v1/movieName/.*"))));

Server Error test

50x

    @Test
    void retrieveAllMovies() {
        //given
        stubFor(get(anyUrl())
                .willReturn(serverError()));
        //then
        assertThrows(MovieErrorResponse.class, ()->moviesRestClient.retrieveAllMovies());

    }

    @Test
    void retrieveAllMovies_503_serviceUnAvailable() {
        //given
        stubFor(get(anyUrl())
                .willReturn(serverError()
                .withStatus(HttpStatus.SERVICE_UNAVAILABLE.value())
                .withBody("Service Unavailable")));
        //then
        MovieErrorResponse movieErrorResponse = assertThrows(MovieErrorResponse.class, ()->moviesRestClient.retrieveAllMovies());
        assertEquals("Service Unavailable", movieErrorResponse.getMessage());

    }

fault

   @Test(expected = MovieErrorResponse.class)
    public void retrieveAllMovies_FaultResponse() {
        //given
        stubFor(get(anyUrl())
                .willReturn(aResponse().withFault(Fault.EMPTY_RESPONSE)));
        //then
        moviesRestClient.retrieveAllMovies();
    }

    @Test(expected = MovieErrorResponse.class)
    public void retrieveAllMovies_RandomDataThenClose() {
        //given
        stubFor(get(anyUrl())
                .willReturn(aResponse().withFault(Fault.RANDOM_DATA_THEN_CLOSE)));
        //then
         moviesRestClient.retrieveAllMovies();
    }

timeout

trzeba dodać tcpklienta

    TcpClient tcpClient = TcpClient.create()
            .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
            .doOnConnected(connection -> {
                connection.addHandlerLast(new ReadTimeoutHandler(5))
                        .addHandlerLast(new WriteTimeoutHandler(5));
            });
    @BeforeEach
    void setUp() {
            int port = wireMockServer.port();
            String baseUrl = String.format("http://localhost:%s/", port);
            System.out.println("baseUrl : " + baseUrl);
            //webClient = WebClient.create(baseUrl);
            webClient = WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient))) //tu dodać
            .baseUrl(baseUrl).build();
            moviesRestClient = new MoviesRestClient(webClient);
            }
    @Test
    void retrieveAllMovies_fixedDelay() {
            //given
            stubFor(get(anyUrl())
            .willReturn(ok().withFixedDelay(10000))); //może też być random
            //then
            assertThrows(MovieErrorResponse.class, ()->moviesRestClient.retrieveAllMovies());
        }

using spring boot configuration

Trzeba jsony umieszczać bezpośrednio w resources. Uwaga, nie można tak ustawić dodatkowych opcji jak opcji jak dynamiczne ustawienie responsa

//junit5 dependencies
    testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.5.1' //to tylko żeby odpalić testy junit4
            testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.1'
            testRuntimeOnly 'org.junit.platform:junit-platform-engine:1.5.1'
            testRuntimeOnly 'org.junit.platform:junit-platform-commons:1.5.1'
            testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.1'


            //wiremock-junit5
            testImplementation "com.github.JensPiegsa:wiremock-extension:0.4.0"

            //wiremock
            testImplementation "org.springframework.cloud:spring-cloud-contract-wiremock:2.1.3.RELEASE"
            testImplementation "com.github.tomakehurst:wiremock-jre8-standalone:2.24.1"

/////
@ExtendWith(SpringExtension.class)
@SpringBootTest
@TestPropertySource(properties= {"movieapp.baseUrl=http://localhost:8091"})
@ExtendWith(WireMockExtension.class)
class MoviesRestClientWireMockExtensionTest {

    @Autowired
    MoviesRestClient moviesRestClient;

    @ConfigureWireMock
    Options options = wireMockConfig()
            .port(8091)
            .notifier(new ConsoleNotifier(true))
            .extensions(new ResponseTemplateTransformer(true));


    @InjectServer
    WireMockServer wireMockServer;

    @Test
    void getAllMovies() {

        //given
        stubFor(get(WireMock.anyUrl())
                .willReturn(WireMock.aResponse()
                        .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                        .withStatus(HttpStatus.OK.value())
                        .withBodyFile("all-movies.json")));
        //whenx
        List<Movie> movieList = moviesRestClient.retrieveAllMovies();
        System.out.println("movieList : " + movieList);

        //then
        assertTrue(!movieList.isEmpty());
    }
⚠️ **GitHub.com Fallback** ⚠️