Kenpachi
Kenpachi

Reputation: 673

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

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?

Upvotes: 12

Views: 21560

Answers (4)

Alexander Taylor
Alexander Taylor

Reputation: 17632

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/

Upvotes: 5

Frankie Drake
Frankie Drake

Reputation: 1388

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

Upvotes: 0

mr.M
mr.M

Reputation: 899

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

Upvotes: 5

M. Deinum
M. Deinum

Reputation: 124441

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

Upvotes: 11

Related Questions