Functional Testing
This article takes the Mock HTTP Endpoints library from sham.software and shows you how you can apply it to the Mule FunctionalTestCase.
sham.software
You can find the library to Mock HTTP Endpoints here:
https://github.com/shamsoftware/sham-http
Maven Dependency
<dependency>
<groupId>software.sham</groupId>
<artifactId>sham-http</artifactId>
<version>1.0.0</version>
<exclusions>
<exclusion>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
</exclusion>
</exclusions>
</dependency>
Shared Resources
To enable the tests to mock an outbound endpoint you must configure the host as localhost.
If your endpoint is HTTP, you can do this by creating a test property file and setting the host.
<http:request-config name="HTTP_Request_Configuration" host="${http.request.host}" port="${http.request.port}" protocol="HTTP" />
Integration Properties
http.request.host=diamondedgeconsulting.com
http.request.port=9001
Test Properties
http.request.host=localhost
http.request.port=9002
If however your endpoint is HTTPS you will need to configure the TLS context to use the mock server’s certificate sham-mock.keystore and sham-mock.truststore provided on the classpath. The password is password.
<http:request-config name="HTTPS_Request_Configuration" host="${https.request.host}" port="${https.request.port}" protocol="HTTPS">
<tls:context>
<tls:trust-store path="${mule.tls.truststore}" password="${mule.tls.truststore.password}"/>
<tls:key-store path="${mule.tls.keystore}" password="${mule.tls.keystore.password}"
keyPassword="${mule.tls.keystore.key.password}"/>
</tls:context>
</http:request-config>
Test Properties
https.request.host=localhost
https.request.port=9002
mule.tls.truststore=sham-mock.truststore
mule.tls.truststore.password=password
mule.tls.keystore=sham-mock.keystore
mule.tls.keystore.password=password
mule.tls.keystore.key.password=password
If your HTTPS endpoint does not have the TLS context element, you may have to create a Mule Configuration File in your test path to redefine the HTTP Request Configuration with the same name as the one in the application path. Anypoint Studio will display this as an error requiring the name to be unique, however, as long as you only load one configuration file for the application and other for testing, then it will run without error at runtime.
Defining the Mock Server
To define the mock server just instantiate an instance of MockHttpServer or MockHttpsServer and pass it as a parameter the port on setup of the class. On tear down, stop the server and clean up any resources.
protected static MockHttpServer server = null;
@BeforeClass
public static void setUpMockResources() {
server = new MockHttpServer(9002);
}
@AfterClass
public static void tearDownMockResources() {
server.stop();
}
Mocking the Endpoint
Once you have set up your server then you can use matchers to match to specific outbound endpoints that you want to mock.
For example here is an outbound HTTPS endpoint:
<http:request config-ref="HTTPS_Request_Configuration" path="/api/path" method="POST">
<http:request-builder>
<http:header headerName="Content-Type" value="application/json" />
<http:query-param paramName="param" value="something" />
</http:request-builder>
<http:success-status-code-validator values="200" />
</http:request>
Here is an example of a test case using the mock:
@Test
public void testMock() throws Exception {
// get mock response
String response = this.loadResourceAsString("src/test/resources/mock/response.json");
assertNotNull("mock response is null", response);
// set up the mock for the outbound endpoint
server.respondTo(post("/api/path")).withStatus(200).withBody(response).withHeader("Content-Type", "application/json");
// get sample request
String payload = this.loadResourceAsString("src/main/api/samples/request.json");
assertNotNull("sample request is null", payload);
// call your functional test
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("Content-Type", "application/json");
MuleMessage response = muleContext.getClient().send(
"https://localhost:9001/api/test",
new DefaultMuleMessage(payload, params, muleContext),
HttpRequestOptionsBuilder.newOptions().method("POST").build()
);
// assert success
assertNotNull("Message should not be null", response);
assertEquals("Incorrect http status code", "200", response.getInboundProperty("http.status").toString());
assertEquals("Incorrect http content type", "application/json", response.getInboundProperty("Content-Type"));
}
This example does the following:
- Reads in the example mock response.
- Uses the mock server to respond to the specific path of the outbound HTTPS endpoint to match:
- The HTTP Method POST
- The mock path.
- Tells the mock to send the following response:
- HTTP Status 200.
- The example mock response.
- The header Content-Type application/json.
- Reads in the sample request.
- Calls the functional test case that contains the outbound HTTPS request.
You can also use the matchers to match more specific details of the request. To read more about this please refer to the documentation on github where you download the library.