The MuleSoft APIKit facilitates REST API implementation from RAML definitions. This toolkit is a framework with the ability to create Mule flows, mapped from the implementation of an API defined within a RAML (see APIKit Tutorial). Implementation of an API in this manner follows best practices, ensuring API-first development.
Developing tests for the flows generated by the APIKit can be done fairly easily using another framework available from MuleSoft, MUnit. This framework provides testing capabilities and is fully integrated with Maven, allowing for automated testing during the build process. It requires a plugin for Anypoint Studio.
There are two ways to test the flows generated by the APIKit. The first is by invoking flow references. The other is to test through the APIKit Router. The latter provides the ability to test the flows as they would be invoked during runtime, based on the RAML, which is the focus of this blog.
Test Suite Creation
To create a test suite for the APIKit flows, in Anypoint Studio right click the APIKit Router and select to create a test suite for RAML:
Once this is done, a test suite is generated that contains one flow for each resource/action/response code combination defined within the RAML (i.e. Employees/get/200, Employees/get/404). Since the same RAML was used to generate the API flows as well as the flows in the test suite, greater code coverage will be achieved.
Along with automatic generation of the test flows, the MUnit test suite creation process also adds assertions within each of these flows that validates the returned http status and payload. The status returned from the invocation of the API flow is compared with an expected response code, as defined when the test flow was generated (resource/action/response code as mentioned above).
The other assertion that is generated automatically is one that compares the actual output returned from the API flow to an expected output. The expected output is generated during the test suite creation using the value specified for the example response (as shown in diagram above). If the example payload defined within your RAML is different then the intended expected results for a given test, a best practice is to define the expected results within an external file, stored in src/test/resources. Use the getResource function to read the file contents as shown below:
<munit:assert-on-equals message="The response payload is not correct!" expectedValue="#[getResource('getEmployeeByIdExpectedResult.json').asString().replaceAll("\\s+","")]" actualValue="#[payload.replaceAll("\\s+","")]" doc:name="assert that - payload is as expected" />
Additional assertions can be added depending on the testing requirements.
MUnit has mocking capabilities that ensure isolation of the test, where changes to external systems have no impact. This is accomplished by creating mocks for endpoints or connectors defined within the API flow. One or more mocks can be added to the test flow, with each defining a mock payload response. Attributes can also be defined to specify the criteria for which the mock will used.
To add a mock to a test, drag a mock component to the test flow:
Within the mock element, choose a message processor that is intended to be mocked. This would be the external endpoint within the API flow for the given test scenario:
<mock:when messageProcessor=".*:.*" doc:name="Mock"> <mock:with-attributes> <mock:with-attribute name="doc:name" whereValue="#['Get Google User']"/> </mock:with-attributes> <mock:then-return payload="#[getResource('getUserExpectedResult.json').asString()]"/> </mock:when>
The payload returned from the mock should reflect the Java type (i.e. String, Map, ArrayList, etc.) that the external endpoint being mocked returns.
Attributes can be added to further qualify the condition(s) for the mock. For example, the following defined attribute will cause the payload to be returned only if the input value, groupKey, is set to the value contained in Value:
XML:<mock:with-attribute name="groupKey" whereValue="email@example.com"/>
For flows that test an error response (i.e. Employees/get/404), a configuration is necessary to alleviate a testing error like “Response code 404 mapped as failure”. Add the status code value range within the HTTP advanced tab as shown below:
XML:<http:success-status-code-validator values="0..599" />
Ensure the MUnit test suite configuration for the mock-connectors and mock-inbounds are both set to “false”:
XML: <munit:config mock-connectors="false" mock-inbounds="false" />
You will know that these are set incorrectly if you receive the following error while invoking the test:
Flow endpoint is null, APIKIT requires a listener ref in each of it's flows
Running the Tests
Within Anypoint Studio, the test suite can be executed by right clicking on the test suite configuration file (i.e. api-apikit-test.xml) found in src/test/munit, select Run As->MUnit. The results of the test can be viewed within the MUnit tab:
Running of these tests within Studio is beneficial during developing/refactoring of the tests, the real power comes during the build process in Maven. Because the framework is fully integrated with Maven, the tests are included in the build, run before the application is packaged. The build will fail if any of the MUnit tests fail, ensuring that only tested code is packaged for deployment, one of the tenets of Continuous Integration.