MuleSoft MUnit Testing Basics
MUnit is MuleSoft's built-in testing framework. It lets you write automated tests for your Mule flows without deploying to a server. MUnit tests run inside Anypoint Studio and in your CI/CD pipeline. Good MUnit coverage catches bugs before they reach production and gives you confidence when making changes to existing flows.
Why Test Mule Applications
Integration bugs are expensive. A missing null check in a transformation crashes the flow for every record in a batch job. A wrong field mapping sends wrong data to Salesforce for thousands of customers. MUnit tests catch these issues in minutes on your laptop instead of in production affecting real users.
Cost of Bug Detection by Stage
Stage | Relative Cost to Fix -------------------|-------------------- Development (MUnit)| 1x (developer fixes it immediately) Code Review | 5x (another developer's time + fixes) QA Testing | 10x (test cycle + fixes + re-test) Staging | 25x (staging deployment + debugging) Production |100x (user impact + emergency fix + post-mortem) MUnit pays for itself many times over.
MUnit Test Structure
An MUnit test file is an XML file that lives in src/test/munit/. Each file contains one or more test suites, and each suite contains individual test cases.
MUnit File Structure
src/
main/
mule/
orders-api.xml ← the flow being tested
test/
munit/
orders-api-test.xml ← MUnit test file
resources/
test-data/
create-order-request.json ← sample input data
expected-order-response.json ← expected output
Anatomy of an MUnit Test Case
Each test case has three phases:
- Given (Before): Set up the test — mock external dependencies, set variables, configure the input payload.
- When (Execute): Call the flow being tested.
- Then (Assert): Verify the output matches expectations.
Test Case Structure Diagram
<munit:test name="createOrder_withValidInput_returns201" >
<!-- GIVEN: set up mocks and input -->
<munit:behavior>
<munit-tools:mock-when processor="db:insert">
<munit-tools:with-attributes>
<munit-tools:with-attribute attributeName="doc:name" whereValue="Save Order"/>
</munit-tools:with-attributes>
<munit-tools:then-return>
<munit-tools:payload value='[{"GENERATED_KEY": 1001}]' mediaType="application/java"/>
</munit-tools:then-return>
</munit-tools:mock-when>
</munit:behavior>
<!-- WHEN: execute the flow -->
<munit:execution>
<munit:set-event>
<munit:payload value='{"product":"Pen","qty":5,"total":7.50}' mediaType="application/json"/>
</munit:set-event>
<flow-ref name="createOrderFlow"/>
</munit:execution>
<!-- THEN: assert the result -->
<munit:validation>
<munit-tools:assert-equals actual="#[output application/java --- vars.httpStatus]" expected="#[201]"/>
<munit-tools:assert-equals actual="#[output application/java --- payload.orderId]" expected="#[1001]"/>
</munit:validation>
</munit:test>
Creating MUnit Tests in Anypoint Studio
Right-click on any flow in the canvas and select MUnit → Create new MUnit Suite. Studio generates a skeleton test file with one test per flow. You then customize each test with realistic input data and proper assertions.
Alternatively, right-click the src/test/munit folder in Package Explorer and select New → MUnit Test to create a fresh test file from scratch.
Mocking External Dependencies
MUnit Mocks replace external dependencies — databases, HTTP calls, Salesforce — with fake implementations during testing. This lets tests run without a real database connection or internet access. Mocks also let you simulate failure conditions like a database being offline.
Mocking a Database Insert
Real behavior: [Database: Insert] → connects to real DB, inserts record
Mocked behavior: [Database: Insert] → returns fake result without touching DB
Mock configuration:
processor: db:insert
doc:name: Save Order
then-return:
payload: [{"GENERATED_KEY": 1001}]
mediaType: application/java
The flow runs exactly as in production, but the DB call
returns the fake result instead of connecting to a real database.
Mocking an HTTP Request
Mock: HTTP call to external shipping API
processor: http:request
doc:name: Get Shipping Rate
then-return:
payload:
'{"rate": 9.99, "carrier": "FedEx", "days": 2}'
mediaType: application/json
attributes:
statusCode: 200
MUnit Assertions
Assertions verify that the flow produced the expected result. MUnit provides several assertion types:
Common Assertions
Assert Equals:
<munit-tools:assert-equals
actual="#[payload.status]"
expected="created"
message="Status should be 'created'"/>
Assert Not Null:
<munit-tools:assert-not-null
value="#[payload.orderId]"
message="Order ID should not be null"/>
Assert That (flexible matching):
<munit-tools:assert-that
expression="#[sizeOf(payload.items)]"
is="#[MunitTools::equalTo(3)]"
message="Should have 3 items"/>
Assert On Error (verify error was thrown):
<munit-tools:assert-on-error
withType="HTTP:NOT_FOUND"
whenCalling="getOrderFlow"
withPayload='{ "customerId": "INVALID-999" }'/>
Running MUnit Tests
In Anypoint Studio, right-click the test file or the src/test/munit folder and select Run MUnit Suite. The MUnit runner starts, executes all tests, and shows results in the MUnit panel at the bottom of the screen.
MUnit Results Panel
MUnit Test Results:
✓ createOrder_withValidInput_returns201 (45ms)
✓ createOrder_withMissingProduct_returns400 (12ms)
✓ createOrder_whenDBOffline_returns503 (8ms)
✗ getOrder_withValidId_returns200 (FAILED)
Expected: { "id": "ORD-001", "status": "shipped" }
Actual: { "id": "ORD-001", "status": "pending" }
Tests: 4 | Passed: 3 | Failed: 1 | Coverage: 78%
Code Coverage in MUnit
MUnit tracks how much of your flow code the tests exercise. The MUnit coverage report shows which components, flows, and lines were visited by tests and which were not. Aim for at least 80% coverage on production flows. MUnit can fail the build if coverage drops below your configured threshold.
