Reputation: 7581
Suppose the application is dependent on a REST service on a external server, http://otherserver.com. For testing, I would like to simulate the external rest call (via Wiremock) within a JUnit environment. Starting a seperate server consumes time and is not easy. Working with WiremockRule looks the right direction. Creating simulation controllers is not an elegant way as Wiremock is available.
E.g. get( "http://otherserver.com/service3/");
PS: of course I know that I can simulate a REST call via Mockito.
Simulating localhost with Wiremock is easy. How can I use that code to simulate other servers and services? I copied parts from the popular Baeldung examples.
public class WireMockDemo {
@Rule
public WireMockRule wireMockRule = new WireMockRule();
@Test
public void wireMockTestJunitOtherServer() {
try {
// **this does not work...**
configureFor("otherserver.com", 8080);
stubFor(get(urlPathMatching("/service2/.*"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("\"testing-library\": \"WireMock\"")));
// Test via simple client
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet request = new HttpGet("http://otherserver:8080/service2/test");
HttpResponse httpResponse = httpClient.execute(request);
String stringResponse = convertHttpResponseToString(httpResponse);
System.out.println( "Response = " + stringResponse);
// Test via JUnit
verify(getRequestedFor(urlEqualTo("/service2/wiremock")));
assertEquals(200, httpResponse.getStatusLine().getStatusCode());
assertEquals("application/json", httpResponse.getFirstHeader("Content-Type").getValue());
assertEquals("\"testing-library\": \"WireMock\"", stringResponse);
} catch( Exception e) {
e.printStackTrace();
}
}
// Support methods
private String convertHttpResponseToString(HttpResponse httpResponse) throws IOException {
InputStream inputStream = httpResponse.getEntity().getContent();
return convertInputStreamToString(inputStream);
}
private String convertInputStreamToString(InputStream inputStream) {
Scanner scanner = new Scanner(inputStream, "UTF-8");
String string = scanner.useDelimiter("\\Z").next();
scanner.close();
return string;
}
}
Upvotes: 6
Views: 10025
Reputation: 12728
TL; DR:
No, you cannot.
What WireMock does, is to establish a Jetty server simulating a remote server you need to send request to. However, it does not change your hosts
file or DNS mapping and automatically "redirect" your real request for remote server to localhost. In tests you still need to send request to localhost
.
What you can do, if you are using Spring Boot, is to create two application.yml
file(or another properties file) in main
and test
package, with same structure of keys, but the value in src/main/resources/application.yml
is the real URL you request(like http://example.com/api
), and that in src/test/resources/application.yml
you put localhost/api
.
By the way, to clarify, MockMvc
is not for simulation of external 3rd party server request that your application depends on, but requests sent to the endpoints of your application. In MockMvc tests, your application is who receives the request, but in WireMock tests, your applications sends request.
Some working example:
// configure static, class-level rule for all tests, like @BeforeClass.
// this launches a Jetty server with configurations
@ClassRule
public static WireMockClassRule classRule = new WireMockClassRule(options().
port(80).httpsPort(443));
// Divide @ClassRule and @Rule,
// to bypass JUnit limitation of "@Rule cannot be static"
@Rule
public WireMockClassRule rule = classRule;
@Test
public void shouldReturnGivenJson() throws Exception {
// stubFor() also works; givenThat() is more TDD-ish
givenThat(post(urlEqualTo("/service2/test")) // <----- note here: without host name
.willReturn(WireMock.aResponse()
.withStatus(HttpStatus.OK.value())
.withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE)
.withBody("{\"status\":\"up\"}")));
// .... your connection here
I suggest to begin with urlEqualTo()
, without messing around with regex. Then you progress to urlMatching()
.
Also, use org.apache.http.util.EntityUtils
to get content from the response. This is the official, built-in way to process the response. And, use a ResponseHandler
because it will consume()
the response without manually cleaning the resources.
Check HttpClient documentation for more details.
Upvotes: 1
Reputation: 8695
Your application code should not have the http://otherserver.com
hardcoded, it should be configurable. When running normally it should point to http://otherserver.com
, when running in test mode it should be pointed to http://localhost:<port>
where <port>
is where you have started your Wiremock server (preferably dynamic to avoid port clashes)
Upvotes: 5