Overriding Properties in MUnit XML and Java for testing

It is very common for any mule application to use external properties files. In this post, we will see how we can override properties values for testing.

Manik MagarManik Magar

It is very common for any mule application to use external properties files. In this post, we will see how we can override properties values for testing. We will also cover how we can write to temporary folder during munit test, disable connector mocking and asserting file existence.

For demonstration purpose, we will have a flow that uses DataWeave to convert xml file into csv and writes to an output folder using file:outbound-endpoint. Let's read the output path from properties file with key explore.mule.target.folder. As we are going to write file during testing, we will need munit not to mock the connectors.

Here is our production code that declares a context:property-placeholder to read properties from src/main/resources/explore-mule.properties

<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:context="http://www.springframework.org/schema/context"
	xmlns:dw="http://www.mulesoft.org/schema/mule/ee/dw"
	xmlns:file="http://www.mulesoft.org/schema/mule/file"
	xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
	xmlns:spring="http://www.springframework.org/schema/beans" version="EE-3.8.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-current.xsd
http://www.mulesoft.org/schema/mule/ee/dw http://www.mulesoft.org/schema/mule/ee/dw/current/dw.xsd
http://www.mulesoft.org/schema/mule/file http://www.mulesoft.org/schema/mule/file/current/mule-file.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd">

    <context:property-placeholder location="explore-mule.properties"/>


    <flow name="properties-testingFlow3">
        <file:inbound-endpoint path="input3" moveToDirectory="output" responseTimeout="10000" doc:name="File"/>
        <dw:transform-message doc:name="Transform Message">
            <dw:input-payload doc:sample="sample_data/empty.xml"/>
            <dw:set-payload resource="classpath:dwl/employees2.dwl"/>
        </dw:transform-message>
        <file:outbound-endpoint path="${explore.mule.target.folder}" outputPattern="output.csv" doc:name="File"/>
    </flow>

</mule>

Overriding Properties in XML

Similar to specifying properties in production code, we can create a copy of properties files under /src/test/resource/env/test/explore-mule.properties and refer to it inside xml using context:property-placeholder.

Below XML MUnit suite shows this option in action. In test explore-mule.properties, we set explore.mule.target.folder=test-output and then verify that output.csv exists after test case executes.

<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:context="http://www.springframework.org/schema/context"
	xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation" xmlns:munit="http://www.mulesoft.org/schema/mule/munit" xmlns:spring="http://www.springframework.org/schema/beans" xmlns:core="http://www.mulesoft.org/schema/mule/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-current.xsd
http://www.mulesoft.org/schema/mule/munit http://www.mulesoft.org/schema/mule/munit/current/mule-munit.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd">

 <context:property-placeholder location="env/test/explore-mule.properties"/>

    <munit:config name="munit" doc:name="MUnit configuration" mock-connectors="false"/>
    <spring:beans>
        <spring:import resource="classpath:sample-flows.xml"/>
    </spring:beans>
    <munit:test name="sample-flows-test-suite-properties-testingFlow3Test" description="Test">
    	<munit:set
			payload="#[getResource('sample_data/employees.xml').asStream()]"
			doc:name="Set Message" mimeType="application/xml" />
        <flow-ref name="properties-testingFlow3" doc:name="Flow-ref to properties-testingFlow3"/>
        <munit:assert-true condition="#[new java.io.File('./test-output/output.csv').exists()]" doc:name="Assert True"/>

    </munit:test>
</mule>

Don't forget to disable connector mocking by adding mock-connectors="false" in munit:config.

Overriding Properties in FunctionalMunitSuite

When you write a munit test case in java using FunctionalMunitSuite, it is more flexible to set properties. When FunctionalMunitSuite creates the mocking configuration during init, it calls a protected method protected Properties getStartUpProperties() to get the properties for tests. Default implementation in FunctionalMUnitSuite returns null but we can easily override this function in our test suite to return an instance of java.util.Properties.

One benefit of this over xml approach is, you get to use power of java while setting properties values. In this example, we will use org.junit.rules.TemporaryFolder to create a temporary folder and set that as a target folder. If we really use this as a junit Rule then JUnit can take care of deleting temporary folder, but here we can use that as rule because getStartUpProperties is called once a testSuite/context initialization so we will keep reference to our properties and folders. So we will also add an AfterClass method to delete this folder.

Below Java code shows this in action. At the end of our test case, we assert that the target temporary folder contains output.csv.

package com.mms.mule.explore;

import java.io.File;
import java.io.IOException;
import java.util.Properties;

import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.AfterClass;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mule.DefaultMuleMessage;
import org.mule.api.MuleEvent;
import org.mule.munit.runner.functional.FunctionalMunitSuite;
import org.mule.transformer.types.MimeTypes;
import org.mule.util.FileUtils;

public class PropertiesTestSuite extends FunctionalMunitSuite {

	private Properties props;
	private static TemporaryFolder tempFolder;

	@Override
	protected String getConfigResources() {
		return "sample-flows.xml";
	}

	@Override
	protected boolean haveToMockMuleConnectors() {
		return false;
	}

	@AfterClass
	public static void cleanup(){
		tempFolder.delete();
	}

	@Override
	protected Properties getStartUpProperties() {
		props = super.getStartUpProperties();
		if(props == null){
			props = new Properties();
		}
		tempFolder = new TemporaryFolder();
		try {
			tempFolder.create();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		String path =tempFolder.getRoot().getAbsolutePath();
		System.out.println("Setting path to - "+ path);
		props.setProperty("explore.mule.target.folder", path);

		return props;
	}

	@Test
	public void testFileWriting() throws Exception{
		String payload = FileUtils.readFileToString(new File(DataWeaveTests.class.getClassLoader().getResource("sample_data/employees.xml").getPath()));

		MuleEvent event = testEvent(payload);
		((DefaultMuleMessage)event.getMessage()).setMimeType(MimeTypes.APPLICATION_XML);

		MuleEvent reply = runFlow("properties-testingFlow3", event);

		MatcherAssert.assertThat(new File(tempFolder.getRoot(), "output.csv").exists(),Matchers.equalTo(Boolean.TRUE));
	}
}

Don't forget to override haveToMockMuleConnectors() and return false to allow file writing.

As an alternative to overriding getStartUpProperties method, you can also create a sample munit xml config with context:properties-placeholder and then use that inside getConfigResources() method.

Test Application Source

Test Application source code is available on Github here.

Conclusion

MUnit provides a very stable environment for testing mule flows. You can easily override your production properties inside MUnit XML as well as Java test suite.