Java Wiremock - 在测试期间模拟外部服务器
Java Wiremock - mock external server during testing
假设应用程序依赖于 外部服务器、http://otherserver.com 上的 REST 服务。为了进行测试,我想在 JUnit 环境中模拟外部 rest 调用(通过 Wiremock)。启动一个单独的服务器会耗费时间而且并不容易。使用 WiremockRule 看起来是正确的方向。创建模拟控制器并不是一种优雅的方式,因为 Wiremock 是可用的。
例如get( "http://otherserver.com/service3/");
PS:我当然知道我可以通过 Mockito 模拟 REST 调用。
使用 Wiremock 模拟本地主机很容易。我如何使用该代码来模拟 其他服务器 和服务?我从流行的 Baeldung 示例中复制了部分内容。
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;
}
}
您的应用程序代码不应 http://otherserver.com
硬编码,它应该是可配置的。当 运行 通常它应该指向 http://otherserver.com
,当 运行 在测试模式下它应该指向 http://localhost:<port>
其中 <port>
是你启动 Wiremock 服务器的地方(最好是动态的,以避免端口冲突)
TL;博士:
不,你不能。
WireMock 所做的是建立一个 Jetty 服务器来模拟您需要向其发送请求的远程服务器。但是,它不会更改您的 hosts
文件或 DNS 映射,并会自动 "redirect" 您对远程服务器的真实请求到本地主机。在测试中你仍然需要发送请求到 localhost
.
如果您使用 Spring 引导,您可以做的是在 main
和 test
包中创建两个 application.yml
文件(或另一个属性文件) ,具有相同的键结构,但 src/main/resources/application.yml
中的值是您请求的真实 URL(如 http://example.com/api
),而 src/test/resources/application.yml
中的值是您输入的 localhost/api
.
顺便说明一下,MockMvc
不是用于模拟 您的应用程序所依赖的外部第 3 方服务器请求 ,而是 请求发送到您的应用程序的端点。在 MockMvc 测试中,您的应用程序接收请求,但在 WireMock 测试中,您的应用程序发送请求。
一些工作示例:
// 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
我建议从 urlEqualTo()
开始,不要乱用正则表达式。然后你进步到 urlMatching()
.
此外,使用 org.apache.http.util.EntityUtils
从响应中获取内容。这是处理响应的官方内置方式。并且,使用 ResponseHandler
因为它会 consume()
响应而无需手动清理资源。
查看 HttpClient 文档了解更多详细信息。
假设应用程序依赖于 外部服务器、http://otherserver.com 上的 REST 服务。为了进行测试,我想在 JUnit 环境中模拟外部 rest 调用(通过 Wiremock)。启动一个单独的服务器会耗费时间而且并不容易。使用 WiremockRule 看起来是正确的方向。创建模拟控制器并不是一种优雅的方式,因为 Wiremock 是可用的。
例如get( "http://otherserver.com/service3/");
PS:我当然知道我可以通过 Mockito 模拟 REST 调用。
使用 Wiremock 模拟本地主机很容易。我如何使用该代码来模拟 其他服务器 和服务?我从流行的 Baeldung 示例中复制了部分内容。
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;
}
}
您的应用程序代码不应 http://otherserver.com
硬编码,它应该是可配置的。当 运行 通常它应该指向 http://otherserver.com
,当 运行 在测试模式下它应该指向 http://localhost:<port>
其中 <port>
是你启动 Wiremock 服务器的地方(最好是动态的,以避免端口冲突)
TL;博士:
不,你不能。
WireMock 所做的是建立一个 Jetty 服务器来模拟您需要向其发送请求的远程服务器。但是,它不会更改您的 hosts
文件或 DNS 映射,并会自动 "redirect" 您对远程服务器的真实请求到本地主机。在测试中你仍然需要发送请求到 localhost
.
如果您使用 Spring 引导,您可以做的是在 main
和 test
包中创建两个 application.yml
文件(或另一个属性文件) ,具有相同的键结构,但 src/main/resources/application.yml
中的值是您请求的真实 URL(如 http://example.com/api
),而 src/test/resources/application.yml
中的值是您输入的 localhost/api
.
顺便说明一下,MockMvc
不是用于模拟 您的应用程序所依赖的外部第 3 方服务器请求 ,而是 请求发送到您的应用程序的端点。在 MockMvc 测试中,您的应用程序接收请求,但在 WireMock 测试中,您的应用程序发送请求。
一些工作示例:
// 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
我建议从 urlEqualTo()
开始,不要乱用正则表达式。然后你进步到 urlMatching()
.
此外,使用 org.apache.http.util.EntityUtils
从响应中获取内容。这是处理响应的官方内置方式。并且,使用 ResponseHandler
因为它会 consume()
响应而无需手动清理资源。
查看 HttpClient 文档了解更多详细信息。