{{announcement.body}}
{{announcement.title}}

Mule 4 WireMock Module

DZone 's Guide to

Mule 4 WireMock Module

In this article, we discuss the value of HTTP test doubles in the context of integration tests and introduce the WireMock module to use in Mule 4

· Big Data Zone ·
Free Resource

In 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:

You may also like:  Mocking REST API With WireMock — Recording and Manual Modes

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:

  1. We're actually sending requests over the wire so we get the confidence that HTTP Requester configuration and subsequent processors are correct.
  2. 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.
  3. 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:

XML
 




xxxxxxxxxx
1


 
1
<dependency>
2
    <groupId>com.ms3-inc.mule</groupId>
3
    <artifactId>mule-wiremock-module</artifactId>
4
    <version>0.5.0</version>
5
    <classifier>mule-plugin</classifier>
6
</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:

XML
 




xxxxxxxxxx
1


 
1
<wiremock:config name="WireMock_Config">
2
    <wiremock:connection host="0.0.0.0" port="8080" protocol="HTTP" resources="src/test/resources" />
3
</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:

JSON
 




xxxxxxxxxx
1
10


 
1
{
2
  "request" : {
3
    "method" : "POST",
4
    "url" : "/service-2"
5
  },
6
  "response" : {
7
    "status" : 204,
8
    "statusMessage": "No Content"
9
  }
10
}



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:

JSON
 




x
10


 
1
{
2
    "request": {
3
        "method": "GET",
4
        "url": "/service-3"
5
    },
6
    "response": {
7
        "status": 200,
8
        "bodyFileName": "service-3-ok-response.xml"
9
    }
10
}



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:

  1. Not use 'Mock When' to bypass HTTP Requesters in your flows.
  2. 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:

XML
 




xxxxxxxxxx
1
30


 
1
<mule xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2
    xmlns:http="http://www.mulesoft.org/schema/mule/http"
3
    xmlns:wiremock="http://www.mulesoft.org/schema/mule/wiremock"
4
    xmlns:munit="http://www.mulesoft.org/schema/mule/munit"
5
    xmlns:munit-tools="http://www.mulesoft.org/schema/mule/munit-tools"
6
    xsi:schemaLocation="http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
7
        http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
8
        http://www.mulesoft.org/schema/mule/wiremock http://www.mulesoft.org/schema/mule/wiremock/current/mule-wiremock.xsd
9
        http://www.mulesoft.org/schema/mule/munit http://www.mulesoft.org/schema/mule/munit/current/mule-munit.xsd
10
        http://www.mulesoft.org/schema/mule/munit-tools  http://www.mulesoft.org/schema/mule/munit-tools/current/mule-munit-tools.xsd">
11
    <munit:config name="munit-integration-test-suite.xml" ></munit:config>
12
    <!-- we tell wiremock to use munit-integration-root directory as its resources root directory -->
13
    <wiremock:config name="wiremockConfig">
14
        <wiremock:connection host="0.0.0.0" port="8080" resources="src/test/resources/munit-integration-root" ></wiremock:connection>
15
    </wiremock:config>
16
    <munit:test name="munit-integration-test-test">
17
        <munit:enable-flow-sources>
18
            <!-- we enable the flow that has the HTTP Listener -->
19
            <munit:enable-flow-source value="system-under-test-implementation-flow" ></munit:enable>
20
        </munit:enable-flow-sources>
21
        <munit:execution>
22
            <!-- we make a live request to /system-under-test -->
23
            <http:request method="GET" url="http://localhost:8081/system-under-test" ></http:request>
24
        </munit:execution>
25
        <munit:validation>
26
            <munit-tools:assert-equals actual="#[attributes.'statusCode']" expected="#[200]" ></munit>
27
            <munit-tools:assert-equals actual="#[payload]" expected="#[read(MunitTools::getResourceAsString('expected-response.json'), 'application/json')]" ></munit>
28
        </munit:validation>
29
    </munit:test>
30
</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:

XML
 




xxxxxxxxxx
1
21


 
1
<!-- here we verify that exactly one request was made to /service-1 -->
2
<wiremock:verify-request config-ref="wiremockConfig" comparison="EQUAL_TO" times="1" 
3
    jsonMapping='#[%dw 2.0
4
output application/json
5
---
6
{
7
    "method" : "GET",
8
    "url" : "/service-1"
9
}]' ></wiremock:verify>
10
<!-- 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 -->
11
<wiremock:verify-request config-ref="wiremockConfig" comparison="EQUAL_TO" times="1"
12
    jsonMapping='#[%dw 2.0
13
output application/json
14
---
15
{
16
    "method" : "POST",
17
    "url" : "/service-2",
18
    "bodyPatterns": [{
19
        "equalTo": "HELLO MUNIT INTEGRATION TEST!"
20
    }]
21
}]' ></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

Topics:
mule 4 ,wiremock ,integration ,tests ,tutorial ,big data

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}