Search code examples
javacucumberwiremock

Java WireMock defining stubs fail in multithreaded tests with Cucumber parallel execution


I have Spring-Boot application and integration tests using Cucumber and WireMock. Executing tests in regular non parallel execution mode works fine without any issues, but when switched to parallel with cucumber.execution.parallel.enabled as true, it started to fail. Exception:

com.github.tomakehurst.wiremock.common.JsonException: 
{
  "errors" : [ {
    "code" : 10,
    "source" : {
      "pointer" : "/code"
    },
    "title" : "Error parsing JSON",
    "detail" : "Unrecognized field \"code\" (class com.github.tomakehurst.wiremock.common.Errors), not marked as ignorable"
  } ]
}
    at com.github.tomakehurst.wiremock.common.JsonException.fromJackson(JsonException.java:53)
    at com.github.tomakehurst.wiremock.common.Json.read(Json.java:60)
    at com.github.tomakehurst.wiremock.client.HttpAdminClient.safelyExecuteRequest(HttpAdminClient.java:519)
    at com.github.tomakehurst.wiremock.client.HttpAdminClient.executeRequest(HttpAdminClient.java:489)
    at com.github.tomakehurst.wiremock.client.HttpAdminClient.executeRequest(HttpAdminClient.java:466)
    at com.github.tomakehurst.wiremock.client.HttpAdminClient.addStubMapping(HttpAdminClient.java:146)
    at com.github.tomakehurst.wiremock.client.WireMock.register(WireMock.java:414)
    at com.github.tomakehurst.wiremock.client.WireMock.register(WireMock.java:409)
    at com.github.tomakehurst.wiremock.client.WireMock.givenThat(WireMock.java:115)
    at com.github.tomakehurst.wiremock.client.WireMock.stubFor(WireMock.java:119)

Does WireMock.stubFor supposed to be a thread-safe operation? Based on exception, looks like nope. If non thread-safe, which workarounds are possible (synchronize / lock all executions of stubFor)?

Example of stub initialization:

WireMock.stubFor(post(urlPathMatching("/test"))
    .withRequestBody(equalToJson(..))
    .willReturn(aResponse()
        .withStatus(200)
        .withHeader("Content-Type", "application/json")
        .withBody(..)));

WireMock is created in the following way:

WireMockServer wireMock = new WireMockServer(options()
    .port(8000)
    .extensions(new ResponseTemplateTransformer(true))
    .notifier(new ConsoleNotifier(true)));
wireMock.start();
configureFor(8000);

version: com.github.tomakehurst:wiremock-jre8-standalone:2.33.0


Solution

  • After substituting class com.github.tomakehurst.wiremock.client.HttpAdminClient in my repo and adding extra logs, it was discovered that response body during stub definition execution is {"code":"404.null.55","message":"Not Found"}. The issue was that request targeted wrong port, it took a default port 8080 instead of configured 8000. In my case, port 8000 was configured for WireMock server and 8080 for Tomcat from Spring Boot test.

    Initially, when Spring context starting, code configures WireMock server with port 8000 from thread main in the following way:

    WireMockServer wireMock = new WireMockServer(options()
        .port(8000)
        .extensions(new ResponseTemplateTransformer(true))
        .notifier(new ConsoleNotifier(true)));
    wireMock.start();
    configureFor(8000);
    

    and later, WireMock.stubFor(..) is invoked from another thread (due to parallel execution mode by Cucumber). Due to implementation inside WireMock, it has thread local defaultInstance, which should be additionally configured with appropriate port from invoked thread.

    So I updated code to invoke WireMock.configureFor(8000); before each invocation of WireMock.stubFor(..) to guarantee that port is configured per each required thread, and it fixed the issue.

    The initial exception that I faced with as JsonException Unrecognized field 'code' is due to not properly handling 404 client error code inside HttpAdminClient, as this error not from Wire Mock server, but might be from any other server (indeed it's a rare case, but still might happen). I will either open an issue or contribute a fix to WireMock to have a descriptive error with explicitly specifying used port in case of 404 error code, so library will handle such cases gracefully.

    Update:

    WireMock contribution change with descriptive exception message has been merged.