FoMS Experts

News / Technology

MOCKER, TESTER, SETTER, SPY

MUnit testing: Basics

MUnit testing: Basics
Share
FacebookGoogle plusLinkedInStumbleuponTwitter
M. Petricevic21.7.2021

MUnit is a testing framework for Mule applications. It helps to create automated tests from existing application units, namely flows.

There is some degree of artistic freedom across the software development sector when defining what constitutes a unit ripe for testing. When it comes to Mule applications, the smallest testable part is a Mule flow.

We will consider an example in order to illustrate the basics of MUnit testing. The example flow we want to test is shown in the image. There are a few things that have to be set right before moving to MUnit.

In the HTTP Listener's properties MIME type is set to application/json and the allowed method to POST. Since it is configured to receive a JSON body containing a property named animal, the Set Variable is used to save this property's value in a variable named zoo-member. The Transform Message creates a new JSON key-value pair which is returned to Postman or a web client.

Notice the output format. Had we not set the HTTP Listener's output MIME type to application/json, we would have received the error "Dataweave Error: You called the function ‘Value Selector’ with these arguments...". The Set Variable component is expecting the JSON format. So, we either set the HTTP Listener's output MIME type to application/json or leave this field blank and let Anypoint Studio and DataWeave do the type matching for us.

The Logger is here for those who prefer Console to Postman and other REST clients. We use the following expression to create a pretty Console output:

#['\n\n\n']
#[payload."zoo-message" default ""]

Testing with Postman

Let us test this unit using Postman or another REST client first. Select the type to JSON and make sure to send the body containing the key-value pair in JSON format via the POST request. Remember, the HTTP Listener is expecting a JSON body and the Set Variable is expecting a value of animal from the payload (payload."animal").

Time to run the project in Anypoint Studio and send the request. Everything seems to be in order. After sending the POST HTTP request, we get the expected response in JSON output. The console output in Anypoint Studio looks fine too.

Testing with MUnit

Now, let us stop the project and create a test for our flow. By right clicking the name of the flow and selecting MUnit - Create blank test for this flow a new XML file main-test-suite with a test flow named main-test-suite-MUnit-exampleFlowTest is created in the src/test/munit folder. Notice the three sections of the test flow: Behavior, Execution and Validation.

The Execution section will always contain a Flow Reference pointing to the flow we want to test when creating tests this way. If we wanted to, we could drop the Flow Reference and use the same processors from the example flow to the Execution and test them (i.e. test the flow within the Execution section). This would defeat the purpose of auto-generated tests by MUnit. Hence the Flow Reference.

What would happen if we ran the test now? The same as if we ran the example flow itself? Right click on the flow's name and select Run MUnit test. After the test has finished running, take a look at the MUnit tab on the bottom-left. If you cannot find it, go to Windows - Show View - Outline. Select the error and maximize the MUnit Errors tab right below. We get an error "Unable to parse empty input, while reading `payload` as JSON". The same error details can be seen when the test is run in the debug mode.

Just to understand what happened, let us run the original flow (not the test flow) again and see what happens if we send an empty body in the HTTP POST request. We get the same error as with MUnit testing! The reason is simple. MUnit mimics the REST client (Postman). It sends the empty body because we haven't specified which body or parameters to send (or rather - to mock) when mimicking the REST client.

There are a few ways around this. One of them is Error handling. After all, this is an error. We will skip this way since it does nothing to help us understand the ways of MUnit.

We might use the Choice router with isEmpty(payload) as the expression in the When scope, as shown in the image. MUnit and testing aside, this is a good trick to know when covering for the possibility of an empty JSON body. Still, we don't want to overuse the Choice router.

If we run the MUnit test for this flow, we will get no errors and the green checkmarks in the flow will show us which route the test took.

Set Event

The best (and the most obvious) way to avoid this error in MUnit is by mocking the HTTP request correctly. In this case, we will use the Set Event operation from the MUnit module before the Flow Reference in the Execution section. What happens if we run the MUnit test for the flow with the Choice router now? The same as without the Set Event. Notice the default Start with an empty event option and that no mock values have been defined yet.

But why didn't we drag and drop the Set Event into the Behavior section? After all, what we are doing is mocking. Because the flow we are testing is expecting a flow-triggering event from which the payload, variables, parameters, etc. will be picked up by the HTTP listener. Think of Set Event as "Event trigger", Set Payload, Attributes, Error and Variable - all in one. So, let's set the expected MIME type and body content in the Payload tab.

The test runs without errors. Looking at the green checkmarks, we can see that the application took the Default route because the payload was not empty. Just as with Postman.

Mocking and validation

This was just an introduction covering the very basics of MUnit. Tests are usually done to mock external events and sources, or to validate something. Some of the more notable MUnit Tools used are Mock When and Assert operations.

More to come

The article you've just read is the first in a series of upcoming articles on MUnit testing. Stay tuned!

Close projects before running tests

If the test is run before stopping the project, the project will be stopped automatically (so that the test can be run). However, Anypoint Studio might not register this properly and a simple Right click - Run the project might not work.

RELATED ARTICLES

OTHER ARTICLES

Relax, some like it soft-coded

KEPT IN ONE PLACE FOR ALL TO USE

Relax, some like it soft-coded

Tough Choices: How to route the app

CHOICE ROUTER IN A MULE APP

Tough Choices: How to route the app

OLDER ARTICLES

Register now