Mule 4 WireMock Module
Join the DZone community and get the full member experience.
Join For FreeIn this post, we'll talk about the value of HTTP test doubles in the context of integration tests and introduce the WireMock module to use in Mule 4.
Integration Tests and WireMock
When writing integration software for distributed systems the most critical part to test is the interaction between those systems. Obviously, this makes integration tests vital. Traditionally, we would test against live systems, but having a local instance of an upstream system or a dedicated test instance can be expensive and/or difficult to coordinate its state between tests.
Instead, we can opt to verify the interactions against test doubles, also known as narrow integration tests. WireMock is a pretty popular HTTP server test double that allows us to configure stubbed responses for particular requests, as well as verifying that the requests it received match what we expect the system under test to have made.
Here are some more in-depth resources worth checking out on integration tests:
Mule 4 Module
For one reason or another, WireMock had not made its way into Mule's homegrown test framework, MUnit. There's been support for Database test doubles with H2 in-memory database, but not for HTTP. So we've addressed that in the Mule 4 WireMock Module.
With the Mule WireMock Module, we can spin up an in-process HTTP Server and run our integration tests against it. WireMock gives us a couple of advantages over MUnit's 'Mock When' processor:
- We're actually sending requests over the wire so we get the confidence that HTTP Requester configuration and subsequent processors are correct.
- WireMock supports conditional responses based on scenarios. This way, we can more easily test behavior that changes the external system's state as it executes.
- With an actual HTTP Server serving requests, we can follow a TDD approach if the real services are not yet available or it's difficult to control their state.
Using the Connector
Add this dependency to your mule application's pom.xml:
xxxxxxxxxx
<dependency>
<groupId>com.ms3-inc.mule</groupId>
<artifactId>mule-wiremock-module</artifactId>
<version>0.5.0</version>
<classifier>mule-plugin</classifier>
</dependency>
Now, the palette and Global Elements should have the WireMock components:
The first thing we want to do is configure the WireMock Server. For that, we use the Global Configuration Element WireMock Config. Here's the simplest config example:
xxxxxxxxxx
<wiremock:config name="WireMock_Config">
<wiremock:connection host="0.0.0.0" port="8080" protocol="HTTP" resources="src/test/resources" />
</wiremock:config>
This tells Mule to start a regular HTTP WireMock server listening on all interfaces at port 8080, this is all the same as the stock HTTP server. The first added attribute is resources
, this is the directory WireMock will use for its mappings
and __files
directories.
To understand how to use the module we must understand what purpose these directories serve to WireMock.
The Mappings Directory
WireMock is primarily meant to be configured with Java code, but it also has the ability to read request/response stub configurations serialized as JSON files. mappings
is the directory within resources
where WireMock will look for and parse *.json files as stubs.
Here's a simple example of stub.json mapping file:
xxxxxxxxxx
{
"request" : {
"method" : "POST",
"url" : "/service-2"
},
"response" : {
"status" : 204,
"statusMessage": "No Content"
}
}
This stub tells WireMock that when a POST request comes int at /service-2
to reply with a 204 No Content.
The __files Directory
At some point, we may want to actually include response bodies in a stub. There's many ways to do that depending on the media type of the response. One of the easiest ways is to use bodyFilename
in the stub:
{
"request": {
"method": "GET",
"url": "/service-3"
},
"response": {
"status": 200,
"bodyFileName": "service-3-ok-response.xml"
}
}
This stub tells WireMock to look for service-3-ok-response.xml
within the __files
directory and use it as the response's body.
Pointing to WireMock
It's important to understand that WireMock is a live HTTP server, meaning we want to:
- Not use 'Mock When' to bypass HTTP Requesters in your flows.
- Point those HTTP Requesters to the WireMock HTTP server.
Normally, things like an external HTTP dependency host and port will be properties put away in a environment-specific file. However, they are provided into the application, so we want to make sure that when running in MUnit, they point to the WireMock server.
In our example, that is localhost:8080, so that's what the HTTP Requesters need to use when going through the implementing flow.
As a side note, it might be a good idea to use "MUnit Dynamic Port." This allows a CI pipeline to run tests from different artifacts and avoid port conflicts.
The Integration Test
Let's assume that our application exposed a service at /system-under-test/service-1
, so our MUnit integration test might look something like this:
xxxxxxxxxx
<mule xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:http="http://www.mulesoft.org/schema/mule/http"
xmlns:wiremock="http://www.mulesoft.org/schema/mule/wiremock"
xmlns:munit="http://www.mulesoft.org/schema/mule/munit"
xmlns:munit-tools="http://www.mulesoft.org/schema/mule/munit-tools"
xsi:schemaLocation="http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/wiremock http://www.mulesoft.org/schema/mule/wiremock/current/mule-wiremock.xsd
http://www.mulesoft.org/schema/mule/munit http://www.mulesoft.org/schema/mule/munit/current/mule-munit.xsd
http://www.mulesoft.org/schema/mule/munit-tools http://www.mulesoft.org/schema/mule/munit-tools/current/mule-munit-tools.xsd">
<munit:config name="munit-integration-test-suite.xml" ></munit:config>
<!-- we tell wiremock to use munit-integration-root directory as its resources root directory -->
<wiremock:config name="wiremockConfig">
<wiremock:connection host="0.0.0.0" port="8080" resources="src/test/resources/munit-integration-root" ></wiremock:connection>
</wiremock:config>
<munit:test name="munit-integration-test-test">
<munit:enable-flow-sources>
<!-- we enable the flow that has the HTTP Listener -->
<munit:enable-flow-source value="system-under-test-implementation-flow" ></munit:enable>
</munit:enable-flow-sources>
<munit:execution>
<!-- we make a live request to /system-under-test -->
<http:request method="GET" url="http://localhost:8081/system-under-test" ></http:request>
</munit:execution>
<munit:validation>
<munit-tools:assert-equals actual="#[attributes.'statusCode']" expected="#[200]" ></munit>
<munit-tools:assert-equals actual="#[payload]" expected="#[read(MunitTools::getResourceAsString('expected-response.json'), 'application/json')]" ></munit>
</munit:validation>
</munit:test>
</mule>
In this test, we're making a live call to /system-under-test
as well as not using 'Mock When' to bypass any HTTP Requesters in the implementing flow.
To assert our service's output, we're using MUnit Tools to assert the status code and body match what we expect. As long as the appropriate stubs are set up in mappings
, the test should pass.
Verifiying Outgoing Requests
In order to increase the level of confidence we have on the integration of this service with external services, we can verify with WireMock that the requests it received match what we expected them to be: That X header's value was as it should, that one particular field in the response body was as it should, etc.
Let's assume that /system-under-test
needed to GET something from an external /service-1
and subsequently POST to an external /service-2
.
For that, we can add something like this after we've asserted the system under test's output:
xxxxxxxxxx
<!-- here we verify that exactly one request was made to /service-1 -->
<wiremock:verify-request config-ref="wiremockConfig" comparison="EQUAL_TO" times="1"
jsonMapping='#[%dw 2.0
output application/json
---
{
"method" : "GET",
"url" : "/service-1"
}]' ></wiremock:verify>
<!-- here we verify that exactly one request was made to /service-2 and that it contained the correct body according to the expected behavior of /system-under-test -->
<wiremock:verify-request config-ref="wiremockConfig" comparison="EQUAL_TO" times="1"
jsonMapping='#[%dw 2.0
output application/json
---
{
"method" : "POST",
"url" : "/service-2",
"bodyPatterns": [{
"equalTo": "HELLO MUNIT INTEGRATION TEST!"
}]
}]' ></wiremock:verify>
Take a look at the module's own integration test for the working MUnit.
In general the Mule 4 module only exposes WireMock's functionality as-is while relying heavily on its JSON representation of stubs and verification objects.
For more details on stubbing responses and verifying requests, see:
And look for "in JSON" or "via JSON API" to see how you can do things using the module.
Integration services User Acceptance Tests
With tests like these, we test the integration with our service from the perspective of our consumers and the integration of the system under test with external HTTP services; effectively serving as a user acceptance test for Mule 4 integration services.
Further Reading
- MUnit With Mule 4.0.
- WireMock: Mock Your REST APIs.
- WireMock With Dynamic Proxies.
Opinions expressed by DZone contributors are their own.
Comments