如何在 JUnit 5 测试中使用 WireMock 的响应模板
How to use WireMock's response template in JUnit 5 Tests
我正在尝试使用 WireMock 的 Response Templating 功能,但它似乎不适用于文档中提供的示例代码。
这是一段示例代码:
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
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 com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import io.restassured.RestAssured;
import org.hamcrest.Matchers;
import org.junit.Rule;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class WireMockTest {
@Rule
public WireMockRule wm = new WireMockRule(options()
.extensions(new ResponseTemplateTransformer(true)));
private WireMockServer wireMockServer;
@BeforeEach
public void setup() {
this.wireMockServer = new WireMockServer(
options().port(8081));
this.wireMockServer.stubFor(get(urlEqualTo("/test-url"))
.willReturn(aResponse()
.withBody("{{request.url}}")
.withTransformers("response-template")));
this.wireMockServer.start();
}
@Test
public void test() {
RestAssured.when()
.get("http://localhost:8081/test-url")
.then()
.log().ifError()
.body(Matchers.equalTo("/test-url"));
}
@AfterEach
public void tearDown() {
wireMockServer.stop();
}
}
预期输出:
测试应该通过。 (这意味着 {{request.url}} 应该替换为 /test-url
作为模板渲染的结果)。
实际输出:
....
java.lang.AssertionError: 1 expectation failed.
Response body doesn't match expectation.
Expected: "/test-url"
Actual: {{request.url}}
我尝试过的事情:
- 由于这些是使用 JUnit 5 API 的测试用例,因此没有添加
@Rule WireMockRule
,而是添加了 .withTransformers("response-template")
。
- 尝试更改测试用例以使用 JUnit 4 API,并添加了
@Rule
public WireMockRule wm = new WireMockRule(options()
.extensions(new ResponseTemplateTransformer(false))
);
(连同 withTransformers
)
3.将WireMockRule
改为
@Rule
public WireMockRule wm = new WireMockRule(options()
.extensions(new ResponseTemplateTransformer(true))
);
(连同变形金刚)
4. 删除了withTransformers
,只保留WireMockRule
。 (JUnit 4)
5. 我也尝试过上述与 JUnit 5 的组合 API。
但是 none 以上变体有效。有什么我想念的吗?
@Rule
方法将不起作用,因为您在 @BeforeEach
中自己创建新规则时忽略了 WireMockServer
created/managed 规则。
您应该删除规则并通过 Options
对象将 @BeforeEach
中的 ResponseTemplateTransformer
添加到 WireMockServer
。
像这样的东西应该可以解决问题(从 Javadoc 判断)。
@BeforeEach
public void setup() {
this.wireMockServer = new WireMockServer(
options()
.extensions(new ResponseTemplateTransformer(false))
.port(8081));
this.wireMockServer.stubFor(get(urlEqualTo("/test-url"))
.willReturn(aResponse()
.withBody("{{request.url}}")
.withTransformers("response-template")));
this.wireMockServer.start();
}
您可以编写自己的 Junit Jupiter 扩展。
举个例子
public class WireMockJunitJupiterExtension implements BeforeAllCallback, AfterAllCallback, ExecutionCondition {
private static final ExtensionContext.Namespace NAMESPACE = create(WireMockJunitJupiterExtension.class);
private static final String WIREMOCK = "wiremock";
@Override
public void afterAll(ExtensionContext context) {
final var server = context.getStore(NAMESPACE).get("wiremock", WireMockServer.class);
server.stop();
server.resetAll();
}
@Override
public void beforeAll(ExtensionContext context) {
final var store = context.getStore(NAMESPACE);
final var server = new WireMockServer(context.getRequiredTestClass().getAnnotation(WireMock.class).port());
findFields(context.getRequiredTestClass(), field -> field.isAnnotationPresent(WithWireMock.class), TOP_DOWN)
.stream()
.map(field -> tryToReadFieldValue(field)
.andThen(o -> call(() -> (MappingBuilder) o))
.getOrThrow(RuntimeException::new)
).forEach(server::stubFor);
store.put(WIREMOCK, server);
server.start();
}
@Override
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
final var shouldRun = context.getRequiredTestClass().isAnnotationPresent(WireMock.class);
return shouldRun ? enabled("Started WireMock server") : disabled("Skipped starting WireMock server");
}
}
注释
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(WireMockJunitJupiterExtension.class)
public @interface WireMock {
int port();
}
@Retention(RetentionPolicy.RUNTIME)
public @interface WithWireMock {
}
以及如何使用它
@WireMock(port = 8077)
public class EventsExporterITTest {
@WithWireMock
static MappingBuilder mappingBuilder = post(urlPathEqualTo("path_uri"))
.withRequestBody(equalToJson(readJsonFromFile("file.json")))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json"));
@Test
public void someTest() {
}
}
汇总了您的所有答案和建议,找到了最适合我的选择:
@Configuration
public class WireMockJunit5Extension implements BeforeAllCallback, AfterAllCallback {
WireMockServer wireMockServer = new WireMockServer();
@Override
public void beforeAll(ExtensionContext context) {
wireMockServer.stubFor(post("/{url1}")
.willReturn(aResponse().withStatus(200)
.withHeader(CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.withBody("{\"status\": \"ok\"}")));
wireMockServer.stubFor(post("/{url2}")
.willReturn(aResponse().withStatus(200)
.withHeader(CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.withBody("{\"status\": \"ok\"}")));
wireMockServer.start();
}
@Override
public void afterAll(ExtensionContext context) {
wireMockServer.stop();
wireMockServer.resetAll();
}
}
2021 年更新,看起来相当于为 JUnit 5 用户创建了 WireMockRule。
class MyTest {
@RegisterExtension
static WireMockExtension wm = WireMockExtension.newInstance()
.options(wireMockConfig()
.dynamicPort()
.dynamicHttpsPort()
.extension(...))
.build();
@Test
testFoo() {
vm.stubFor(...);
doSomethingUsingUrl(vm.getRuntimeInfo.getHttpBaseUrl() + "/test")
vm.verify(...);
}
}
(wm.getRuntimeInfo().getHttpBaseUrl()
包括 wm.getRuntimeInfo().getHttpPort()
- 是的)
请参阅此处的 Advanced usage - programmatic
部分:
我正在尝试使用 WireMock 的 Response Templating 功能,但它似乎不适用于文档中提供的示例代码。
这是一段示例代码:
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
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 com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import io.restassured.RestAssured;
import org.hamcrest.Matchers;
import org.junit.Rule;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class WireMockTest {
@Rule
public WireMockRule wm = new WireMockRule(options()
.extensions(new ResponseTemplateTransformer(true)));
private WireMockServer wireMockServer;
@BeforeEach
public void setup() {
this.wireMockServer = new WireMockServer(
options().port(8081));
this.wireMockServer.stubFor(get(urlEqualTo("/test-url"))
.willReturn(aResponse()
.withBody("{{request.url}}")
.withTransformers("response-template")));
this.wireMockServer.start();
}
@Test
public void test() {
RestAssured.when()
.get("http://localhost:8081/test-url")
.then()
.log().ifError()
.body(Matchers.equalTo("/test-url"));
}
@AfterEach
public void tearDown() {
wireMockServer.stop();
}
}
预期输出:
测试应该通过。 (这意味着 {{request.url}} 应该替换为 /test-url
作为模板渲染的结果)。
实际输出:
....
java.lang.AssertionError: 1 expectation failed.
Response body doesn't match expectation.
Expected: "/test-url"
Actual: {{request.url}}
我尝试过的事情:
- 由于这些是使用 JUnit 5 API 的测试用例,因此没有添加
@Rule WireMockRule
,而是添加了.withTransformers("response-template")
。 - 尝试更改测试用例以使用 JUnit 4 API,并添加了
@Rule
public WireMockRule wm = new WireMockRule(options()
.extensions(new ResponseTemplateTransformer(false))
);
(连同 withTransformers
)
3.将WireMockRule
改为
@Rule
public WireMockRule wm = new WireMockRule(options()
.extensions(new ResponseTemplateTransformer(true))
);
(连同变形金刚)
4. 删除了withTransformers
,只保留WireMockRule
。 (JUnit 4)
5. 我也尝试过上述与 JUnit 5 的组合 API。
但是 none 以上变体有效。有什么我想念的吗?
@Rule
方法将不起作用,因为您在 @BeforeEach
中自己创建新规则时忽略了 WireMockServer
created/managed 规则。
您应该删除规则并通过 Options
对象将 @BeforeEach
中的 ResponseTemplateTransformer
添加到 WireMockServer
。
像这样的东西应该可以解决问题(从 Javadoc 判断)。
@BeforeEach
public void setup() {
this.wireMockServer = new WireMockServer(
options()
.extensions(new ResponseTemplateTransformer(false))
.port(8081));
this.wireMockServer.stubFor(get(urlEqualTo("/test-url"))
.willReturn(aResponse()
.withBody("{{request.url}}")
.withTransformers("response-template")));
this.wireMockServer.start();
}
您可以编写自己的 Junit Jupiter 扩展。
举个例子
public class WireMockJunitJupiterExtension implements BeforeAllCallback, AfterAllCallback, ExecutionCondition {
private static final ExtensionContext.Namespace NAMESPACE = create(WireMockJunitJupiterExtension.class);
private static final String WIREMOCK = "wiremock";
@Override
public void afterAll(ExtensionContext context) {
final var server = context.getStore(NAMESPACE).get("wiremock", WireMockServer.class);
server.stop();
server.resetAll();
}
@Override
public void beforeAll(ExtensionContext context) {
final var store = context.getStore(NAMESPACE);
final var server = new WireMockServer(context.getRequiredTestClass().getAnnotation(WireMock.class).port());
findFields(context.getRequiredTestClass(), field -> field.isAnnotationPresent(WithWireMock.class), TOP_DOWN)
.stream()
.map(field -> tryToReadFieldValue(field)
.andThen(o -> call(() -> (MappingBuilder) o))
.getOrThrow(RuntimeException::new)
).forEach(server::stubFor);
store.put(WIREMOCK, server);
server.start();
}
@Override
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
final var shouldRun = context.getRequiredTestClass().isAnnotationPresent(WireMock.class);
return shouldRun ? enabled("Started WireMock server") : disabled("Skipped starting WireMock server");
}
}
注释
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(WireMockJunitJupiterExtension.class)
public @interface WireMock {
int port();
}
@Retention(RetentionPolicy.RUNTIME)
public @interface WithWireMock {
}
以及如何使用它
@WireMock(port = 8077)
public class EventsExporterITTest {
@WithWireMock
static MappingBuilder mappingBuilder = post(urlPathEqualTo("path_uri"))
.withRequestBody(equalToJson(readJsonFromFile("file.json")))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json"));
@Test
public void someTest() {
}
}
汇总了您的所有答案和建议,找到了最适合我的选择:
@Configuration
public class WireMockJunit5Extension implements BeforeAllCallback, AfterAllCallback {
WireMockServer wireMockServer = new WireMockServer();
@Override
public void beforeAll(ExtensionContext context) {
wireMockServer.stubFor(post("/{url1}")
.willReturn(aResponse().withStatus(200)
.withHeader(CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.withBody("{\"status\": \"ok\"}")));
wireMockServer.stubFor(post("/{url2}")
.willReturn(aResponse().withStatus(200)
.withHeader(CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.withBody("{\"status\": \"ok\"}")));
wireMockServer.start();
}
@Override
public void afterAll(ExtensionContext context) {
wireMockServer.stop();
wireMockServer.resetAll();
}
}
2021 年更新,看起来相当于为 JUnit 5 用户创建了 WireMockRule。
class MyTest {
@RegisterExtension
static WireMockExtension wm = WireMockExtension.newInstance()
.options(wireMockConfig()
.dynamicPort()
.dynamicHttpsPort()
.extension(...))
.build();
@Test
testFoo() {
vm.stubFor(...);
doSomethingUsingUrl(vm.getRuntimeInfo.getHttpBaseUrl() + "/test")
vm.verify(...);
}
}
(wm.getRuntimeInfo().getHttpBaseUrl()
包括 wm.getRuntimeInfo().getHttpPort()
- 是的)
请参阅此处的 Advanced usage - programmatic
部分: