Study guide

11.3 MUnit 2.x Testing: Unit Tests, Mocking Connectors & Assertions

Salesforce Certified MuleSoft Developer I Exam

Track your progress as you read

Finished sections get a check mark.

1 Chapter 1: Creating Application Networks & API-Led Connectivity

1.1 The IT Delivery Gap & API-Led Connectivity Architecture 1.2 Application Network Foundations & Center for Enablement (C4E) 1.3 Anypoint Platform Architecture & Core Components

2 Chapter 2: Designing APIs with RAML & API Designer

2.1 RAML 1.0 Fundamentals, Data Types & Resource Hierarchy 2.2 Reusable API Fragments, ResourceTypes & Traits 2.3 API Mocking Service, Documentation & Anypoint Exchange Publishing

3 Chapter 3: Accessing and Modifying Mule Events

3.1 Mule 4 Event Architecture: Message, Payload, Attributes & Variables 3.2 Flow Variable Management & Target Variables Pattern 3.3 Accessing Event Metadata, Inbound Attributes & Context Properties

4 Chapter 4: Structuring Mule Applications

4.1 Modular Flow Composition: Flows, Subflows & Private Flows 4.2 Configuration Property Files & Environment Management 4.3 Secure Properties Tool, Encryption & Maven Project Packaging

5 Chapter 5: Building API Implementation Interfaces

5.1 APIkit Scaffolding, Router Configuration & Interface Generation 5.2 HTTP Listener Configuration, Path Matching & Query/URI Parameters 5.3 Interface vs. Implementation Separation & Backend Flow Wiring

6 Chapter 6: Using Connectors & External Systems

6.1 Database Connector: Select, Insert, Update, Stored Procedures & Watermarking 6.2 HTTP Request Connector: Headers, Query Params, Response Timeouts & Target Variables 6.3 Salesforce Connector, JMS Messaging & File/FTP Connectors

7 Chapter 7: Processing Records & Batch Integration

7.1 For Each Scope: Payload Iteration, Batch Size & Streaming Data 7.2 Batch Job Architecture: Input, Process, and Complete Phases 7.3 Batch Step Accept Expression, Failure Handling & Batch Aggregator 7.4 Object Store: Persisting Data Between Flow Executions

8 Chapter 8: Transforming Data with DataWeave 2.0

8.1 DataWeave 2.0 Syntax, Type System & Selectors 8.2 Core Functions: map, filter, pluck, reduce, groupBy & orderBy 8.3 Custom Functions, Format Transformations & Type Coercion 8.4 Calling Mule Flows from DataWeave & Reusing DataWeave Modules

9 Chapter 9: Routing Events & Flow Control

9.1 Choice Router: Conditional Expressions & Default Routing 9.2 Scatter-Gather Router: Parallel Execution, Aggregation & Error Traps 9.3 Async Scope, Round Robin & First Successful Routing 9.4 Validating Mule Events with the Validation Module

10 Chapter 10: Handling Errors & Exception Management

10.1 Mule 4 Error Object Hierarchy & Custom Error Types 10.2 On-Error Continue vs. On-Error Propagate Mechanisms 10.3 Try Scope Exception Isolation & Global Error Handling Strategies

11 Chapter 11: Debugging and Troubleshooting Mule Applications

11.1 Logger Configuration, Categories & Thread/Context Diagnostics 11.2 Anypoint Studio Debugger: Breakpoints, Expression Watches & Step-Through 11.3 MUnit 2.x Testing: Unit Tests, Mocking Connectors & Assertions

12 Chapter 12: Deploying and Managing APIs and Integrations

12.1 CloudHub 1.0 & 2.0 Architecture: Workers, vCores & Clustering 12.2 Runtime Manager: Application Management, Alerts, Insights & Logs 12.3 API Manager: Auto-Discovery, API Gateway, SLA Tiers & Standard Policies

11.3 MUnit 2.x Testing: Unit Tests, Mocking Connectors & Assertions

Key Takeaways Explain This Topic AI

MUnit 2.x is the native unit and integration testing framework for Mule 4, organized into XML test suites under src/test/munit and executed via Anypoint Studio or the munit-maven-plugin in CI/CD pipelines.

Every <munit:test> follows a three-phase lifecycle: <munit:behavior> (preconditions, mocks, spies), <munit:execution> (event setup and flow invocation), and <munit:validation> (assertions and call verifications).

The <munit-tools:mock-when> processor intercepts calls to external connectors (such as HTTP Request or Database) and substitutes simulated payloads, attributes, or simulated errors without invoking backend systems.

The <munit-tools:verify-call> processor validates the exact execution frequency of a processor (e.g., verifying a database insert was called times='1' or an alert processor was called times='0' ).

MUnit coverage reports calculate flow, file, and application processor coverage, enabling automated build failure enforcement via Maven quality gates when thresholds are unmet.

Last updated: August 2026

MUnit 2.x Testing: Unit Tests, Mocking Connectors & Assertions

MUnit 2.x is the native testing framework designed specifically for Mule 4 applications. Fully integrated with Anypoint Studio and Apache Maven, MUnit allows developers to write automated unit and integration tests for flows, sub-flows, and error handlers. By mocking external endpoints, spying on intermediate message states, and verifying execution paths, MUnit ensures high software quality and prevents regressions in CI/CD automation.

1. MUnit 2.x Architecture & Project Structure

MUnit tests are authored as standard Mule XML configuration files located in the project's src/test/munit directory. Test artifacts, sample payloads, and mock responses are stored in src/test/resources . +-------------------------------------------------------------------------+

  • | MUNIT PROJECT STRUCTURE |
  • | |
  • | my-mule-app/ |
  • | ├── src/main/mule/ <-- Application Flow XML Files |
  • | │ ├── orders-api.xml |
  • | │ └── global-configs.xml |
  • | ├── src/main/resources/ <-- Application Properties & Log4j 2 |
  • | ├── src/test/munit/ <-- MUnit Test Suite XML Files |
  • | │ ├── orders-api-test-suite.xml |
  • | │ └── error-handling-test-suite.xml |
  • | ├── src/test/resources/ <-- Mock Payloads, Samples, Schemas |
  • | │ ├── sample-order-request.json |
  • | │ └── mock-db-customer-response.json |
  • | └── pom.xml <-- munit-maven-plugin & Dependencies|
  • +-------------------------------------------------------------------------+

Lifecycle Scopes in an MUnit Test Suite:

<munit:before-suite> / <munit:after-suite> : Executes once before/after all test cases in the suite file (ideal for spinning up embedded test brokers or database fixtures).

<munit:before-test> / <munit:after-test> : Executes before/after each individual <munit:test> case in the file (ideal for resetting variables or cleaning temporary state).

<munit:test> : Defines an individual automated test case.

2. The Three-Phase Test Lifecycle ( <munit:test> )

Every MUnit test case is organized into three distinct, sequential execution sections: +-------------------------------------------------------------------------+

  • | MUNIT 3-PHASE TEST LIFECYCLE |
  • | |
  • | +-----------------------------------------------------------------+ |
  • | | 1. BEHAVIOR (<munit:behavior>) | |
  • | | - Configure Mock processors (<munit-tools:mock-when>) | |
  • | | - Configure Spies (<munit-tools:spy>) | |
  • | | - Define preconditions and stubbed return values | |
  • | +-----------------------------------------------------------------+ |
  • | | |
  • | v |
  • | +-----------------------------------------------------------------+ |
  • | | 2. EXECUTION (<munit:execution>) | |
  • | | - Initialize Event (<munit:set-event>) | |
  • | | - Invoke target flow under test (<flow-ref>) | |
  • | +-----------------------------------------------------------------+ |
  • | | |
  • | v |
  • | +-----------------------------------------------------------------+ |
  • | | 3. VALIDATION (<munit:validation>) | |
  • | | - Assert payload & variables (<munit-tools:assert-that>) | |
  • | | - Verify execution counts (<munit-tools:verify-call>) | |
  • | +-----------------------------------------------------------------+ |
  • +-------------------------------------------------------------------------+
  • Comprehensive Example of an MUnit Test Case: <munit:test name='createOrder-validPayload-returns201'
  • description='Verify that valid order submission creates record and returns HTTP 201'
  • doc:id='test-001'>
  • <!-- PHASE 1: BEHAVIOR -->
  • <munit:behavior>
  • <!-- Mock the Database Insert Processor -->
  • <munit-tools:mock-when processor='db:insert'>
  • <munit-tools:with-attributes>
  • <munit-tools:with-attribute attributeName='doc:name' whereValue='Insert Order Record' />
  • </munit-tools:with-attributes>
  • <munit-tools:then-return>
  • <munit-tools:payload value='#[{ affectedRows: 1 }]' mediaType='application/java' />
  • </munit-tools:then-return>
  • </munit-tools:mock-when>
  • </munit:behavior>
  • <!-- PHASE 2: EXECUTION -->
  • <munit:execution>
  • <!-- Construct Inbound Mule Event -->
  • <munit:set-event doc:name='Set Initial Request Event'>
  • <munit:payload value="#[MunitTools::getResourceAsString(&apos;sample-order-request.json&apos;)]"
  • mediaType='application/json' />
  • <munit:attributes value="#[{ headers: { &apos;client_id&apos;: &apos;test-client-123&apos; } }]" />
  • </munit:set-event>
  • <!-- Trigger Flow Under Test -->
  • <flow-ref name='post:\orders:api-config' />
  • </munit:execution>
  • <!-- PHASE 3: VALIDATION -->
  • <munit:validation>
  • <!-- Assert Resulting Payload Status -->
  • <munit-tools:assert-that expression='#[payload.status]'
  • is="#[MunitTools::equalTo(&apos;CREATED&apos;)]"
  • message='Expected order status to be CREATED' />
  • <!-- Assert HTTP Status Code Attribute -->
  • <munit-tools:assert-that expression='#[vars.httpStatus]'
  • is='#[MunitTools::equalTo(201)]'
  • message='Expected HTTP Status code 201' />
  • <!-- Verify the DB Insert was called exactly once -->
  • <munit-tools:verify-call processor='db:insert' times='1'>
  • <munit-tools:with-attributes>
  • <munit-tools:with-attribute attributeName='doc:name' whereValue='Insert Order Record' />
  • </munit-tools:with-attributes>
  • </munit-tools:verify-call>
  • </munit:validation>
  • </munit:test>
  • 3. Core Mocking, Spying & Verification Processors

Processor Placement Scope Primary Purpose Key Attributes / Child Elements

<munit-tools:mock-when> <munit:behavior> Intercepts a message processor and returns stubbed data or errors without invoking external backends. processor , <with-attributes> , <then-return> (payload, attributes, variables, errorType)

<munit-tools:spy> <munit:behavior> Inspects the Mule event immediately before and immediately after a processor executes. processor , <before-call> (assertions), <after-call> (assertions)

<munit-tools:verify-call> <munit:validation> Verifies how many times a specific processor was executed during the test run. processor , times='N' , atLeast='N' , atMost='N'

<munit:set-event> <munit:execution> Constructs a simulated inbound Mule event with custom payload, attributes, and variables. <payload> , <attributes> , <variables>

<munit-tools:assert-that> <munit:validation> Evaluates a DataWeave expression against a Hamcrest matcher condition. expression , is , message

<munit-tools:assert-equals> <munit:validation> Compares actual value against expected value for exact equality. actual , expected , message

4. Mocking Exceptions & Testing Error Handlers

MUnit allows testing error handling flows without intentionally sabotaging external networks. By configuring <munit-tools:mock-when> to throw an error, developers simulate any Mule error type (e.g., HTTP:CONNECTIVITY , DB:BAD_SQL_SYNTAX , APP:INVALID_ACCOUNT ). <!-- Testing Error Handling with Mocked Exception -->

  • <munit:test name='postOrder-dbDown-handlesConnectivityError' doc:id='test-002'>
  • <munit:behavior>
  • <!-- Mock DB to throw HTTP:CONNECTIVITY error -->
  • <munit-tools:mock-when processor='db:insert'>
  • <munit-tools:then-return>
  • <munit-tools:error errorTypeId='DB:CONNECTIVITY' />
  • </munit-tools:then-return>
  • </munit-tools:mock-when>
  • </munit:behavior>
  • <munit:execution>
  • <munit:set-event>
  • <munit:payload value="#[{ orderId: &apos;ORD-123&apos;, total: 99.00 }]" mediaType='application/json' />
  • </munit:set-event>
  • <flow-ref name='post:\orders:api-config' />
  • </munit:execution>
  • <munit:validation>
  • <!-- Verify on-error-continue transformed payload to graceful 503 error message -->
  • <munit-tools:assert-equals actual='#[vars.httpStatus]' expected='#[503]' />
  • <munit-tools:assert-that expression='#[payload.error.code]'
  • is="#[MunitTools::equalTo(&apos;SERVICE_UNAVAILABLE&apos;)]" />
  • </munit:validation>
  • </munit:test>

Handling on-error-propagate with expectedErrorType :

If the target flow uses on-error-propagate , the error is re-thrown by the flow reference. In MUnit, unhandled re-thrown errors will fail the test unless the test explicitly declares expectedErrorType : <munit:test name='validateOrder-invalidPayload-throwsCustomError'

  • expectedErrorType='ORDER:INVALID_PAYLOAD'>
  • <munit:execution>
  • <munit:set-event>
  • <munit:payload value='#[{ badData: true }]' mediaType='application/json' />
  • </munit:set-event>
  • <flow-ref name='validate-order-flow' />
  • </munit:execution>
  • </munit:test>
  • 5. Hamcrest Matchers Reference ( MunitTools:: )

MUnit includes built-in Hamcrest matchers accessed via the MunitTools:: DataWeave module in <munit-tools:assert-that> :

Matcher Function Example Expression Description

equalTo(value) is="#[MunitTools::equalTo(&apos;ACTIVE&apos;)]" Asserts exact structural and value equality.

not(matcher) is='#[MunitTools::not(MunitTools::equalTo(0))]' Inverts the specified matcher logic.

nullValue() is='#[MunitTools::nullValue()]' Asserts that the expression evaluates to null.

notNullValue() is='#[MunitTools::notNullValue()]' Asserts that the expression is not null.

hasSize(number) is='#[MunitTools::hasSize(5)]' Asserts array or collection element count.

hasItem(value) is="#[MunitTools::hasItem(&apos;ADMIN&apos;)]" Asserts collection contains the specified item.

everyItem(matcher) is='#[MunitTools::everyItem(MunitTools::notNullValue())]' Asserts every item in collection matches condition.

containsString(str) is="#[MunitTools::containsString(&apos;Success&apos;)]" Asserts target string contains substring.

startsWith(str) is="#[MunitTools::startsWith(&apos;ORD-&apos;)]" Asserts string begins with target prefix.

isEmpty() is='#[MunitTools::isEmpty()]' Asserts collection, string, or map is empty.

6. MUnit Coverage Reports & Maven Quality Gates

Automated CI/CD pipelines use the munit-maven-plugin in pom.xml to execute test suites and generate code coverage reports. +-------------------------------------------------------------------------+

  • | MUNIT COVERAGE METRICS |
  • | |
  • | 1. APPLICATION COVERAGE: |
  • | Percentage of all message processors executed across the entire |
  • | application during test suite execution. |
  • | |
  • | 2. RESOURCE / FILE COVERAGE: |
  • | Percentage of message processors executed within a specific XML |
  • | configuration file (e.g., orders-api.xml). |
  • | |
  • | 3. FLOW COVERAGE: |
  • | Percentage of message processors executed within an individual |
  • | Mule flow or sub-flow. |
  • +-------------------------------------------------------------------------+
  • Configuring Coverage Thresholds in pom.xml : <plugin>
  • <groupId>com.mulesoft.munit.tools</groupId>
  • <artifactId>munit-maven-plugin</artifactId>
  • <version>2.3.14</version>
  • <executions>
  • <execution>
  • <id>test</id>
  • <phase>test</phase>
  • <goals>
  • <goal>test</goal>
  • <goal>coverage-report</goal>
  • </goals>
  • </execution>
  • </executions>
  • <configuration>
  • <coverage>
  • <runCoverage>true</runCoverage>
  • <failBuild>true</failBuild>
  • <requiredApplicationCoverage>80</requiredApplicationCoverage>
  • <requiredResourceCoverage>75</requiredResourceCoverage>
  • <requiredFlowCoverage>70</requiredFlowCoverage>
  • <formats>
  • <format>html</format>
  • <format>json</format>
  • <format>console</format>
  • </formats>
  • </coverage>
  • </configuration>
  • </plugin>

If <failBuild>true</failBuild> is set and the calculated coverage falls below any configured threshold ( requiredApplicationCoverage , requiredResourceCoverage , or requiredFlowCoverage ), the Maven build command ( mvn clean test ) terminates with a build failure, preventing deployment of untested code.

7. Exam Watch: Core MUnit Scenarios

Important

Placement of <munit-tools:mock-when> On the exam, <munit-tools:mock-when> and <munit-tools:spy> processors must always be placed inside the <munit:behavior> section. Placing mocks inside <munit:execution> or <munit:validation> is an invalid configuration.

  • Warning
  • Testing on-error-continue vs on-error-propagate

If a flow handles an exception with on-error-continue , the flow completes successfully and returns a transformed message. Do not set expectedErrorType on the <munit:test> .

If a flow handles an exception with on-error-propagate , the error is re-thrown. The <munit:test> must declare expectedErrorType='NAMESPACE:IDENTIFIER' to pass.

Tip

Verifying Conditional Branches with times='0' Use <munit-tools:verify-call processor='smtp:send' times='0' /> to verify that an alert email was never sent when testing a valid non-error execution path.