using-munit
2016-11-04T16:11:28.000Z
Using MUnit today to test some flows. Simply click on a flow you want to test and Mule Studio will generate a default configuration. Mule has a bunch of different assertion message processors you can use to validate test results. The challenge I faced was what to test, and where to start. The testing strategy that seemed to work for me was to take a bottom up approach. I started to test all the simple flows first and for these I mostly asserted the outputs were set correctly on flow-vars, or payloads. I used mostly assert-on-equals:
<munit:assert-on-equals message="#[''\nvalue 2 was not set correctly'']"
expectedValue="#[''value 2'']"
actualValue="#[flowVars.response]"
doc:name="Assert Equals"/>
and assert-payload-equals:
<munit:assert-payload-equals message="oops, wrong payload!"
expectedValue="#[''response_payload_1'']"
doc:name="Assert Payload"/>
After testing the simple flows you want to test all paths if using a choice message processor, to make sure the the correct flow is called for each case in the choice message processor. For these types of test cases I mostly used verify-call message processor, with this processor you can use to to verify the correct flow is being called here is a simple example:
<mock:verify-call messageProcessor="mule:sub-flow"
doc:name="Verify Call" times="1">
<mock:with-attributes>
<mock:with-attribute whereValue="#[matchContains(''exampleSub_Flow2'')]"
name="name"/>
</mock:with-attributes>
</mock:verify-call>
There are a some important things to notice here. The first is the messageProcessor attribute which I set to mule:sub-flow, this is the way to configure verify-call, to verify a correct flow is being called, the other is the whereValue where I use #[matchContains(''exampleSub_Flow2'')]
always use a matchContains when testing flows that are being called.
Next after the separate parts/flows/sub-flows and the different paths are tested, you want to then test the other flows that use these smaller flows. Here you only want to test the logic inside these flows and not the flow-ref they use which you have already tested using the bottom up approach.
So in these instances you will mock those other flow refs and set-payload settings. Here are 2 examples mocking those sections of a flow:
<mock:when messageProcessor="mule:set-payload" doc:name="Mock">
<mock:with-attributes>
<mock:with-attribute whereValue="#[''Set Original Payload'']"
name="doc:name"/>
</mock:with-attributes><mock:then-return payload="#[]"/>
</mock:when>
Here we are mocking a set-payload message processor because we are not testing that section of the flow but what follows. Here is an example of mocking a flow-ref:
<mock:when messageProcessor="mule:flow" doc:name="Mock">
<mock:with-attributes>
<mock:with-attribute whereValue="#[''exampleFlow2'']" name="name"/>
</mock:with-attributes>
<mock:then-return payload="#[]">
<mock:invocation-properties>
<mock:invocation-property key="my_variable" value="#[''1'']"/>
</mock:invocation-properties></mock:then-return>
</mock:when>
Here we are mocking a flow and performing any operations that flow does, here that exmpleFlow2 sets the my_variable flow variable to the value of ''1'', but we don''t want to test this so we mock that section of the test. Next we can call the actual flow we are testing and then assert we get back the payload we are expecting using an assertion like the following:
<munit:assert-payload-equals message="oops, wrong payload!"
expectedValue="#[''response_payload_1'']"
doc:name="Assert Payload"/>
The final testing is to test the main flow without mocking any sub-flows/set-payloads and just testing the functionality by setting the correct inbound-properties that flow expects, here is an example of setting an http query param for a flow that expects http query parms:
<munit:set payload="#['''']" doc:name="Set Message url_key:payload_1">
<munit:inbound-properties>
<munit:inbound-property key="http.query.params"
value="#[[''url_key'':''payload_1'']]"/>
</munit:inbound-properties>
</munit:set>
In summary, creating tests in Mule Studio is easy, the difficulty comes when choosing an approach to follow and how to start. Using a bottom up approach worked for me, start by testing all the simple well defined flows, that only do one thing, then testing any choice message processors, making sure we test all paths. Next tackle more complex flows that employ the flows that were already tested and for those mock any flow-refs we already tested and simply test any paths in those flows.
Finally do functional testing of the main flow by mocking only the initial message and asserting the ending payload is correct, for different initial messages you expect to process.