How to use WireMock's response template in JUnit 5 Tests

Viewed 12343

I'm trying to use the Response Templating feature of WireMock, but it does not seem to work with the sample piece of code provided in the docs.

This is a sample piece of code:


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();
  }
}

Expected Output:

Tests should pass. (meaning the {{request.url}} should be substituted with /test-url as a result of template rendering).

Actual Output:

....

java.lang.AssertionError: 1 expectation failed.
Response body doesn't match expectation.
Expected: "/test-url"
  Actual: {{request.url}}

Things I've tried:

  1. Since these are test cases using JUnit 5 API, did not add @Rule WireMockRule, instead added the .withTransformers("response-template").
  2. Tried changing the test cases to use JUnit 4 API, and added
@Rule
public WireMockRule wm = new WireMockRule(options()
    .extensions(new ResponseTemplateTransformer(false))
);

(along with withTransformers)
3. Changed the WireMockRule to

@Rule
public WireMockRule wm = new WireMockRule(options()
    .extensions(new ResponseTemplateTransformer(true))
);

(along with withTransformers)
4. Removed the withTransformers only keeping the WireMockRule. (JUnit 4)
5. I've also tried the above combination with JUnit 5 API too.

But none of the above variations worked. Is there anything that I'm missing?

4 Answers

The @Rule approach won't work because you are ignoring the WireMockServer created/managed by te rule as you are creating a new one yourself in the @BeforeEach.

You should remove the rule and add the ResponseTemplateTransformer in your @BeforeEach to the WireMockServer through the Options object.

Something like this should do the trick (judging from the 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();
  }

You can write your own Junit Jupiter extension.

Just to give you the example

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");
    }
}

Annotations

@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(WireMockJunitJupiterExtension.class)
public @interface WireMock {
    int port();
}
@Retention(RetentionPolicy.RUNTIME)
public @interface WithWireMock {
}

And how to use it

@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() {
    }
}

2021 update, looks like an equivalent to WireMockRule was created for the JUnit 5 users.

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() includes wm.getRuntimeInfo().getHttpPort() - yay)

See Advanced usage - programmatic section here:

http://wiremock.org/docs/junit-jupiter/

Aggregating all your answers and suggestions, found best choice for me:

@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();
    }
}
Related