Munit - tests in Groovy

If I haven’t said that earlier, i will do it now - Munit is great! Being able to easly create isolated unit tests, mocking only selected steps in the flow and verifying not only flow output but also value of payload and properties in the middle of flow processing is priceless for developers.

Based on recent releases of library looks like Mulesoft promotes writing Munit tests in XML, still allowing to do it in Java. There are some limitations - not all XML features are available - but personally I can live without them. In my opinion tests written that way are more flexible and easier to maintain than the one in XML. Still i got some frustration with Java language itself. Groovy looks like a good solution for them. I decided to give it a try and write some Munit tests in Groovy.

1. Maven set-up

  • install Groovy eclipse plugin
  • create folder src/test/groovy
  • add to pom.xml groovy maven plugin
<plugin>
    <groupId>org.codehaus.gmavenplus</groupId>
    <artifactId>gmavenplus-plugin</artifactId>
    <version>1.5</version>
    <executions>
        <execution>
            <goals>
                <goal>addTestSources</goal>
                <goal>testCompile</goal>
            </goals>
        </execution>
    </executions>
</plugin>
  • and don’t forget to add Munit to pom.xml!
<properties>
...
	  <munit.version>1.1.1</munit.version>
		<mule.munit.support.version>3.7.0</mule.munit.support.version>
</properties>
...
<dependencies>
...
<dependency>
		   <groupId>com.mulesoft.munit</groupId>
		    <artifactId>mule-munit-support</artifactId>
		    <version>${mule.munit.support.version}</version>
		    <scope>test</scope>
		</dependency>
		<dependency>
		    <groupId>com.mulesoft.munit</groupId>
		    <artifactId>munit-runner</artifactId>
		    <version>${munit.version}</version>
		    <scope>test</scope>
		</dependency>
</dependencies>

Thanks to those changes Groovy classes with tests are automatically compiled and can be executed as regular Junit tests using standard Eclipse plugin. log-sms-eclipse-project

2. Sample application for tests

It is hard to write tests without application, so let’s create one. I can easily testify that I finally was able to follow TDD methodology - I had code for the sample tests before figuring out what tested application would really do!

Finally I decided to create simple application implementing API adding activity history to Salesforce for received SMS.

log-sms-flow

API is definined in RAML, request contains 3 input parameters in JSON format. There is an integration with Salesforce, so application has to handle communication issues and errors. Logic is simple but requires some test coverage. If you want to play with app remember to update /src/main/app/mule-app.properties with your personal Salesforce username/password/securityToken.

3. Groovy tests

All 4 tests are in LogSMSTest class. Let me show you how Groovy can make Mule unit testing more convenient for developer.

Easy to read

Being able to avoid not needed parenthesis and semicolons increase test readability - just take a look how fluently test below can be read and understood.

1
2
3
4
5
6
7
8
9
10
@Test
	public void shouldRejectInvalidRequest() {

		//when											
		MuleMessage result = send '{"phone":"6309999999"}'

		//then
		assert result.getInboundProperty('http.status') == 400
		assert result.getPayloadAsString() == '{ "exception" : {"code": "INVALID_REQUEST", "message": "Bad request" }}'		
	}

Easy to debug

There is no need for extra logging or debugging in Groovy tests - failed assertions provide automatically all needed information to identify problem with failed test. You can see below that assertion expected http.status code 401 while flow returned 400.

Assertion failed:

assert result.getInboundProperty('http.status') == 401
       |      |                                 |
       |      400                               false

       org.mule.DefaultMuleMessage
       {
         id=6a8ff2d0-03ed-11e6-ac8a-ecf4bb679c19
         payload=org.glassfish.grizzly.utils.BufferInputStream
         correlationId=<not set>
         correlationGroup=-1
         correlationSeq=-1
         encoding=windows-1252
         exceptionPayload=<not set>

       Message properties:
         INVOCATION scoped properties:
         INBOUND scoped properties:
           connection=close
           content-length=71
           content-type=application/json
           date=Sat, 16 Apr 2016 16:08:11 GMT
           http.reason=
           http.status=400
           mule_encoding=windows-1252
         OUTBOUND scoped properties:
         SESSION scoped properties:
       }

Support for multiline string and template

Imagine that you have complex request that can be used in many test cases that differ only with single value - happens often with parametrized tests. In Java you will either have to create multiple copies of requests in external resource or import 3rd party template library. Groovy gives it for free and there is no need to read template from external file (although it is maybe not so good practice after all - your test code can become messy)

1
2
3
4
5
6
7
8
9
10
11
12
	Template requestTemplate = new groovy.text.SimpleTemplateEngine().createTemplate('''
		 {"LogSMSRequest" :
			{
			"occuredAt": "October 12, 2015 at 08:05PM",
			"whoId": "${whoId}",
			"text":"Hello World!"
			}
			 }
		''')
	//when
	MuleMessage result = send requestTemplate.make([whoId: "00000"]).toString()
	

Easy JSON parsing

Assertions with string comparision of whole result works nice only for simple responses. When complex XML or JSON data is returned, then it has to be parsed in order to create assertions for single field value. Groovy helps with that task with XMLSlurper or JSONSlurper classes. Instances of those classes parse string with XML or JSON to map of map and allow accessing values using keys as field names.

For sample response:

1
2
3
4
{ "exception":
	{"code":"INVALID_OPERATION_WITH_EXPIRED_PASSWORD",
	"message": "The users password has expired, you must call SetPassword before attempting any other API operations" }
}   

assertions could look like this:

1
2
3
4
def json = new JsonSlurper().parseText(result.getPayloadAsString())

json.exception.code == "INVALID_OPERATION_WITH_EXPIRED_PASSWORD"
json.exception.message == "The users password has expired, you must call SetPassword before attempting any other API operations"

Inline maps

Mocking up data for mule application tests requires creating and populating a lot of maps. Not so convenient in pure Javam where you have to declare map instance first, then put each key-walue pair in separate line of code. Groovy helps a lot with that! Take a look on sample inline maps in test below. First one is used together with JSONBuilder to fluently create sample JSON request. Second inline map is used to set up mock data returned by Salesforce call. MEL language treats objects as map, so if you want to mock object to check internal processing there is no need to create real object instance (salesforce api fault in case below), but simply create properly populated map instance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Test
public void shouldSuccessForValidRequest() {

	//given
	def newTaskId = 'newTaskId'

	//JSON Builder sample
	def requestBuilder = new groovy.json.JsonBuilder()		
	requestBuilder.LogSMSRequest {
			occuredAt "October 12, 2015 at 08:05PM"
			whoId "validId"
			text "Hello World!"
		}						
	//when

	whenMessageProcessor("create").ofNamespace("sfdc").
	withAttributes(['doc:name': 'Create Task']).thenReturn(muleMessageWithPayload(
				//inline map
				[[id:newTaskId,
				  errors:[]
				]]
				))

	MuleMessage result = send requestBuilder.toString()

	//then
	assert result.getInboundProperty('http.status') == 200
	assert result.getPayloadAsString() =~ newTaskId
}

4. Summary

Munit tests in Groovy work good, but not perfect. For example I wish there is an easy way to combine Munit with Spock. Unfortunately, both testing frameworks requires test class to extend its specific parent class, so both of them can’t work together. Additionally, comparing to Munit tests in XML, I miss Mule Studio integration, especially test coverage report.

Nevertheless, after trying writing tests in Groovy it is hard to go back to writing tests in Java. Give it a try and see how it works for you. The application and tests are available in github.

Written on April 16, 2016