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('sample-order-request.json')]"
- mediaType='application/json' />
- <munit:attributes value="#[{ headers: { 'client_id': 'test-client-123' } }]" />
- </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('CREATED')]"
- 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: 'ORD-123', 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('SERVICE_UNAVAILABLE')]" />
- </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('ACTIVE')]" 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('ADMIN')]" 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('Success')]" Asserts target string contains substring.
startsWith(str) is="#[MunitTools::startsWith('ORD-')]" 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.