You may consider what was said in another question: mulesoft - mUnits and Error Handling - How to mock error error.muleMessage - Stack Overflow
Here a practical example:
Considering this subflow to be tested and have 100% coverage
Where I need to evaluate the error from HTTP Request like:
#[ ( error.errorMessage.attributes.statusCode == 400 ) and ( error.errorMessage.payload.message contains 'Account already exists!' ) ]
I will need a structure of HTTP Listener and HTTP Request during the MUnit Test with configurations specific to the MUnit Test Suite ℹ️ it's important to consdier keep in the same file, as the MUnit executes each file separately and can't see other flows in different files inside src/test/munit
<!-- 1. A dynamic port is reserved for the test listener to avoid conflicts. -->
<munit:dynamic-port
propertyName="munit.dynamic.port"
min="6000"
max="7000" />
<!-- 2. The listener runs on the dynamic port defined above. -->
<http:listener-config
name="MUnit_HTTP_Listener_config"
doc:name="HTTP Listener config">
<http:listener-connection
host="0.0.0.0"
port="${munit.dynamic.port}" />
</http:listener-config>
<!-- This request config targets the local listener. -->
<http:request-config name="MUnit_HTTP_Request_configuration">
<http:request-connection
host="localhost"
port="${munit.dynamic.port}" />
</http:request-config>
<!-- 3. This flow acts as the mock server. It receives requests from the utility flow and generates the desired HTTP response. -->
<flow name="munit-util-mock-http-error.listener">
<http:listener
doc:name="Listener"
config-ref="MUnit_HTTP_Listener_config"
path="/*">
<http:response
statusCode="#[(attributes.queryParams.statusCode default attributes.queryParams.httpStatus) default 200]"
reasonPhrase="#[attributes.queryParams.reasonPhrase]">
<http:headers>
<![CDATA[#[attributes.headers]]]>
</http:headers>
</http:response>
<http:error-response
statusCode="#[(attributes.queryParams.statusCode default attributes.queryParams.httpStatus) default 500]"
reasonPhrase="#[attributes.queryParams.reasonPhrase]">
<http:body>
<![CDATA[#[payload]]]>
</http:body>
<http:headers>
<![CDATA[#[attributes.headers]]]>
</http:headers>
</http:error-response>
</http:listener>
<logger
level="TRACE"
doc:name="doc: Listener Response will Return the payload/http status for the respective request that was made to mock" />
<!-- The listener simply returns whatever payload it received, but within an error response structure. -->
</flow>
<!-- 4. This is the reusable flow called by 'then-call'. Its job is to trigger the listener. -->
<flow name="munit-util-mock-http-error.req-based-on-vars.munitHttp">
<try doc:name="Try">
<http:request
config-ref="MUnit_HTTP_Request_configuration"
method="#[vars.munitHttp.method default 'GET']"
path="#[vars.munitHttp.path default '/']"
sendBodyMode="ALWAYS">
<!-- It passes body, headers and query params from a variable, allowing dynamic control over the mock's response. -->
<http:body>
<![CDATA[#[vars.munitBody]]]>
</http:body>
<http:headers>
<![CDATA[#[vars.munitHttp.headers default {}]]]>
</http:headers>
<http:query-params>
<![CDATA[#[vars.munitHttp.queryParams default {}]]]>
</http:query-params>
</http:request>
<!-- The error generated by the listener is naturally propagated back to the caller of this flow. -->
<error-handler>
<on-error-propagate doc:name="On Error Propagate">
<!-- Both error or success will remove the variables for mock, so it doesn't mess with the next operation in the flow/subflow that are being tested. -->
<remove-variable
doc:name="munitHttp"
variableName="munitHttp" />
<remove-variable
doc:name="munitBody"
variableName="munitBody" />
</on-error-propagate>
</error-handler>
</try>
<remove-variable
doc:name="munitHttp"
variableName="munitHttp" />
<remove-variable
doc:name="munitBody"
variableName="munitBody" />
</flow>
Then create the test and add both flows in the Enabled Flow Sources
For each mock, it will need to define a respective flow to make the request using the variables suggested and create the error response. Remember to define the then-call property to call it.
Here an example of flow
<!-- 3. This flow acts as a test-specific setup, preparing the data for the mock. -->
<flow name="impl-test-suite.mock-http-req-external-400.flow">
<ee:transform
doc:name="munitHttp {queryParams: statusCode: 400 } } ; munitBody ;"
doc:id="904f4a7e-b23d-4aed-a4e1-f049c97434ef">
<ee:message></ee:message>
<ee:variables>
<!-- This variable will become the body of the error response. -->
<ee:set-variable variableName="munitBody">
<![CDATA[%dw 2.0 output application/json --- { message: "Account already exists!" }]]>
</ee:set-variable>
<!-- This variable passes the desired status code to the listener via query parameters. -->
<ee:set-variable variableName="munitHttp">
<![CDATA[%dw 2.0 output application/java ---
{
path : "/",
method: "GET",
queryParams: {
statusCode: 400,
},
}]]>
</ee:set-variable>
</ee:variables>
</ee:transform>
<!-- 4. Finally, call the reusable utility flow to trigger the mock listener. -->
<flow-ref
doc:name="FlowRef req-based-on-vars.munitHttp-flow"
name="munit-util-mock-http-error.req-based-on-vars.munitHttp" />
</flow>
Repository with this example: AndyDaSilva52/mule-example-munit-http-error: MuleSoft Example for MUnit test case that returns proper Mule error (i.e., HTTP:NOT_FOUND) with HTTP status code (i.e., 404 not found) and proper HTTP message body.