Home » Testing within CI/CD using MUnit in MuleSoft
Actueel + +

Testing within CI/CD using MUnit in MuleSoft

CI/CD requires automated testing. MUnit enables unit testing for MuleSoft APIs from the very beginning—from RAML to production.

This blog was written by SynTouch, a subsidiary of SUPERP.

In modern software development, Continuous Integration and Continuous Deployment (CI/CD) have become the standard for delivering new features quickly and reliably. Within this automated process, testing is no longer an optional step, but an essential part of every successful release. For MuleSoft developers, this means that testing APIs and integration flows must be built in from the start, and that is exactly where MUnit plays a crucial role.

Unit Testing vs. Integration Testing

Unit tests focus on testing individual parts of an application such as a specific Mule flow or component, in isolation from other systems or dependencies. They are fast, isolated, and ideal for validating logic. Integration tests, on the other hand, simulate how different components work together, including interactions with external systems, databases, or APIs. With MUnit, you can get close to integration testing by using mocking, spies, and assertions to verify behavior.

But integration tests are certainly still needed to test the entire API-driven chain.

For integration testing, you need an environment where all applications are running so you can test everything from the user experience to the system application. If your environment allows it, you’ll want to expand this further to an end-to-end (E2E) test, where you test the entire flow from the starting application to the end application.

There’s no getting around data lineage.

In-depth look

Now we’ll focus specifically on MUnit to enable early testing. We’re assuming you already have some experience with MUnit, perhaps from completing the Mulesoft Certified Developer 1 certification. Mulesoft likes to showcase the MUnit test recorder as an example of easy testing, but it takes some effort to set up an entire environment for testing. A quicker way to get started with testing is the feature that lets you use your RAML to create a bare-bones Munit that will test your RAML contract.

The more examples you include in your RAML, the more it will help you create a Munit test. Here is an example Munit test that checks the status code and payload.

HTTP status code 500 is the expected response here, along with a sample message. The payload copies the files from the RAML into the ‘scaffold’ and is referenced using the ‘MunitTools::getResourceAsString’ function. You can obtain the ‘scaffold’ by right-clicking on the Apikit router connector and having it create this Munit suite. As the name suggests, this retrieves the file’s content as a string without converting it to a type and compares it with the result of the execution. The ‘write’ function is used for this, but there are risks involved because you are performing another transformation on the result. Here, you must pay close attention to the configuration properties for content types such as CSV and XML.

General tips

When using a “Spy,” you can check values while the test is running. The catch is that your test will still pass even if it skips this “Spy.” That’s why it’s a good idea to always include a “Verify call” that references the same thing as the “Spy.”

When using the Validation section of a Munit test, you need to be careful when testing error scenarios. If you use the Expected error type, the Validation step is skipped entirely, so you can no longer perform any validation. A suitable alternative to the Expected error type is a ‘Try’ statement with an ‘On error continue’. You still need to verify that the error actually occurs. For example, by using an “assert that” on a payload or a “verify call” on a connector in the error handler.

In the Execution, a “Set Event” is often used, for example, when you generate the code using the recorder or from the RAML. The downside of this component is that you have to enter the values in a single line in the UI menu and load your payloads as strings or import DataWeaves. It does provide a good overview of payloads, attributes, errors, and variables, but it is less readable for debugging. An alternative is a simple “transform message”; if you still reference a file for the source code within it, you can also reuse that code across different tests.

Ideally, you’d like to build Munits using data files, but depending on the content, there might be a reference to a date or a dynamic property that prevents this. In that case, you’ll need to use a Dataweave script to replace these values before the payload comparison. Functions like these are often useful to have in shared resources.

If you need the same “mock when” component in multiple tests, it’s helpful to place it in a separate flow and use it with a “flow ref” in the Behavior. You can also place multiple “mock when” components in a separate flow if a particular flow has many branches. This simplifies the overview in the Munit test due to the rigid layout within Anypoint Studio.

With parameterization, you can reuse entire Munit tests and run different tests using only a YAML input file. See the link for an example of how to set this up.

You also have a “Before suite scope,” a “Before test scope,” and an “After” variant that allow you to run setup or cleanup tasks before or after all tests or each individual test. This could involve, for example, always passing the same payload or setting up or cleaning up an object store. For example, you can use a single input for all tests in a ‘MUnit Test Suite file,’ with each test containing its own specific adjustments. This way, you don’t have to keep track of as many changes in separate files.

In Anypoint Studio, you can run a Munit test suite, and in the UI, you can use “Test Resources” to specify which files the suite should run. With “Test,” you can run one or a few specific tests to get quick results. You can also do this in the command line using ‘mvn clean test -Dmunit.test=test-suite.xml#YOUR_TEST_NAME_HERE’.

Finally, Mulesoft has also created a cookbook for Munits that provides many examples and serves as an extension of the introductory material covered in Mulesoft’s own courses.

Workshop

In this section, we’ll set up a test based on a RAML you create. A RAML is often the starting point for building an API, and Mulesoft has built most of its functionality around it.

To try this yourself, you’ll need a RAML; we’ll download one from Mulesoft. You can view the example via this link.

There, you’ll find the file `api-designer-experience-api-1.1.5-raml.zip`. We’ll extract this file and place it in a new Mule project.

Here, you can left-click on `api.raml` to launch “Generate Flows from Local Rest API,” which creates the `api.xml` file with the Apikit router configured and all flows containing the HTTP requests.

Here, you can right-click on the “APIkit Router” and select “Create Test Suite for api.xml from API Specification” to generate the Munit test suite. This creates the file apiapikit-test.xml in the src/test/munit folder, and two folders, scaffolder/request and scaffolder/response, are created under src/test/resources to hold all the necessary files.

You can use the Munit you create here to get started with test-driven development, since a complete RAML specification already provides all your initial validation.

Together with SynTouch

For integration, we partner with SynTouch. Whether you need help designing a robust integration architectureor streamlining application connections, together we deliver solutions that work in your environment.

SynTouch

Altijd als eerste op de hoogte?
Volg ons op LinkedIn!

Lincedin icon

Mis geen update of event!
Abonneer je op onze nieuwsbrief en hoor als eerste over onze nieuwste updates, klantverhalen en events.

Skip form

Gerelateerde artikelen