How Wiremock Helps with GraphQL #
Dependence on test data #
It is common for Microservices to call other downstream services. The coordination to make sure all services have reliable test data in-sync with other services is exponentially hard. Thus the need for contract-driven development, which allows simultaneous development of services that depend on each other.
Another positive side effect of writing tests using Wiremock: having deterministic CICD regression testing.
Testing of network settings #
Wiremock allows for more test coverage because it validates the application properties loaded by the Spring application context - differently unit tests or other frameworks such as mockwebserver.
Testing negative Cases #
Wiremock allows the testing of negative use cases because it supports stubbing of alternative HTTP response codes, random response bodies, and slow network responses.
Code #
Wiremock can be used to isolate and parallelize the development of contract-driven features because it allows the stubbing of responses based on contracts. See examples below using Kotlin:
WireMock.stubFor allows creating stubs for REST endpoints based on headers, request path, and body:
wiremockServer.stubFor(post(urlEqualTo("/endpoint"))
.withRequestBody(equalToJson("""{"entries": [{"key": "value"]}"""))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json")
.withBody("""{
|"payload": {
| "entries": [
| {
| "key": "value"
| }
| ]
|},
|"status": 200
}""".trimMargin())))
Junit5 + Spring initializer to start/stop wiremock:
class WireMockContextInitializer : ApplicationContextInitializer<ConfigurableApplicationContext> {
override fun initialize(applicationContext: ConfigurableApplicationContext) {
val wmServer = WireMockServer(WireMockConfiguration().port(9095))
wmServer.start()
applicationContext.beanFactory.registerSingleton("wireMock", wmServer)
applicationContext.addApplicationListener {
if (it is ContextClosedEvent) {
wmServer.stop()
}
}
}
}
Full example using RestAssured, Springboot, and Junit 5 to test the localserver.
@SpringBootTest(classes = [Application::class],
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = ["externalEndpointBaseUrl=http://localhost:9095/endpoint"])
@ContextConfiguration(initializers = [WireMockContextInitializer::class])
class ApiTest(
@LocalServerPort val port: Int,
@Autowired val wmServer: WireMockServer
) {
@Test
fun `get external call information and return in the graphql response`() {
wmServer.stubFor(post(urlEqualTo("/endpoint"))
.withRequestBody(equalToJson("""{"body": [{"key": "value"]}"""))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json")
.withBody("""{
|"payload": {
| "entries": [
| {
| "key": "value"
| }
| ]
|},
|"status": 200
}""".trimMargin())))
RestAssured.given()
.filter(ResponseLoggingFilter())
.contentType(ContentType.JSON)
.body("{\"query\":\"{\\n object(param1: { entry: \\\"value\\\"}) " +
"{\\n key\\n }\\n}\\n \"}")
.`when`().log().all()
.post("http://localhost:$port/graphql")
.then().log().all()
.statusCode(200)
.body("data.entries.key", equalTo("value"))
.log().all()
wmServer.verify(postRequestedFor(urlEqualTo("/entrypoint"))
.withRequestBody(equalToJson("""{"body": [{"key": "value"]}"""))
)
}
}