Wirmock, Webclient - rlip/java GitHub Wiki
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
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
}
}
stubFor(tuGetAnboPostAlboInne(tuPoniższe))
- anyUrl()
- urlPathEqualTo(dokladnyUrl)
- urlPathMatching("movieservice/v1/movie/[0-9]+") - tu można regex
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")));
ż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")));
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")));
}
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"
}
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')}}"
}
@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/.*"))));
@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());
}
@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();
}
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());
}
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());
}