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.

Leave a Comment

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