Blog Posts

Mule Functional Testing: How to Mock HTTP Endpoints

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.

Leave a Reply

Your email address will not be published. Required fields are marked *