Mule : Functional Testing
This page last changed on Nov 25, 2006 by ross.
Testing MuleAs Mule is light-wieght and embeddable it is easy to run a Mule Server inside a testcase. In fact Mule provides an abstract JUnit testcase called org.mule.tck.FunctionalTestCase for running Mule inside a testcase and manages the lifecycle of the server. The Mule TCK aslo contains a number of other spporting classes for functionally testing Mule code. These are described in more detail in the following sections. The FunctionalTestCaseThe FunctionalTestCase is a base test case for tests that initialize Mule using a configuration file. Generally we would make our Mule test cases extend the FunctionalTestCase so that we could use its functionality. The default configuration builder used in the FunctionalTestCase is the MuleXmlConfigurationBuilder. For this to work you need to have the mule-modules-builders jar on your classpath. This enables us to just enter the name of the mule configuration file we are using in the method getConfigResources and the FunctionalTestCase would automatically call the default builder to configure Mule. protected String getConfigResources() { return"mule-conf.xml"; } Here, you can specify a comma-separated list of config files. If you want to use a different configuration builder, just overload the getBuilder() method of this class to return the type of builder you want to use with your test. protected String getBuilder() { return new SpringConfigurationBuilder(); } Quick Links to Examples
The FunctionalTestComponent:The FunctionalTestComponent is a component which, as the name implies, is used in the testing environment. The component comprises of two methods: the onCall method and the onReceive method that basically do the same thing
What the FunctionalTestComponent does in either one of the methods is to take the message passed to it (either from the UMOEventContext or from the Object) and transform it into a String. It will then create a message that it will send back to the caller. It also checks whether either one of its three properties are set and acts accordingly. The Properties of the FunctionalTestComponent:
By default the FunctionalTestComponent returns the received message and appends the word "Received" to it, i.e. if we send the message "Hello World" it returns the message "Hello World Received". The returnMessage property enables us to replace this default message with one of our own. We can set our message in the returnMessage property in one of two ways: We could set this property on the component in the mule-config.xml file <mule-descriptor name="myComponent" implementation="org.mule.tck.functional.FunctionalTestComponent"> <inbound-router> <endpoint address="vm://test"/> </inbound-router> <outbound-router> <router className="org.mule.routing.outbound.OutboundPassThroughRouter"> <endpoint address="vm://testout"/> </router> </outbound-router> <properties> <property value="Customized Return Message" name="returnMessage"/> </properties> </mule-descriptor> We could set the property programmatically in our test case. FunctionalTestComponent myComponent = new FunctionalTestComponent(); single.setReturnMessage("Customized Return Message"); The eventCallback PropertyThe eventCallback is a property which refers to the EventCallback interface. The scope of the Event Callback is to be able to get a component we are using and to control it. This is especially useful when we have beans configured in Spring that we need to exercise some form of control on, example setting properties at runtime instead of in the configuration. To be able to use the eventCallback from our test case, first we need to make our test case implement the EventCallback interface. Then we would need to implement the single method that this interface exposes, i.e. the eventReceived method. In the example below, we are using the eventReceived method to get a message from the event context. Then we transform the component received as an input parameter into a FunctionalTestComponent by typecasting it and we set the returnMessage property discussed previously on the FunctionalTestComponent at runtime. public void eventReceived(UMOEventContext context, Object Component) throws Exception { UMOMessage message = context.getMessage(); FunctionalTestComponent fc = (FunctionalTestComponent) Component; fc.setReturnMessage("Customised Return Message"); } For Information regarding how to use the EventCallback with components configured as Spring beans click on the following link: Using the eventCallback with a bean configured in Spring The throwException PropertyThe throwException property enables us to throw a MuleException from inside the FunctionalTestComponent. By default this property is set to "false" so all we need to do is set it to "true". There are two ways we can go about doing this. 1. We could set this property on the component in the mule-config.xml file <mule-descriptor name="myComponent" implementation="org.mule.tck.functional.FunctionalTestComponent"> <inbound-router> <endpoint address="vm://test"/> </inbound-router> <properties> <property value="true" name="throwException"/> </properties> </mule-descriptor> 2. We could use the QuickConfigurationBuilder to build the configuration in our test case and set the property programmatically. FunctionalTestComponent myComponent = new FunctionalTestComponent(); single.setThrowException(true); FunctionalTestNotificationListener:When an event is received, the FunctionalTestComponent fires a notification informing us that the event has been received. It is up to us to set up a listener (the FunctionalTestNotificationListener) on our test to capture this notification. To be able to do so, we must first make our test case implement the FunctionalTestNotificationListener interface. Then we must implement the method exposed by this listener, which is the onNotification. In the example below, we check the notification.getAction to see whether it is the FunctionalTestNotification fired by the FunctionalTestComponent. If it is, we print it out to the console. public void onNotification(UMOServerNotification notification) { if (notification.getAction() == FunctionalTestNotification.EVENT_RECEIVED) { System._out_.println("Event Received"); } } Now, in order for our listener to start listening for notifications, we must register it. When using the QuickConfigurationBuilder, which will soon be discussed, the listener is registered like so: builder.getManager().registerListener(this,"myComponent"); Alternatively we could set it directly on the MuleManager. MuleManager.getInstance().registerListener(listener); Overloading the Default Configuration Builder:Sometimes it is useful to be able to able to overload the default configuration builder to be able to create the configuration builder through our test case. In such a case we would use the QuickConfigurationBuilder. With this type of configuration we must be careful to do the following:
The following is an example where we use the QuickConfigurationBuilder to overload the default configuration builder. Note: The method setUp() is the implementation of the method exposed by the TestCase class which our test class extends. protected void setUp() throws Exception { builder = new QuickConfigurationBuilder(); // we create our MuleManager. builder.createStartedManager(false, null); // we create a "SINGLE" endpoint and set the address to vm://single UMOEndpoint vmSingle = builder.createEndpoint("vm://Single","SingleEndpoint", true); // we create a FunctionalTestComponent and call it myComponent FunctionalTestComponent myComponent = new FunctionalTestComponent(); builder.registerComponentInstance(myComponent, "SINGLE",vmSingle.getEndpointURI()); } A Complete Example using the QuickConfigurationBuilder:We will now proceed in building a complete example using the FunctionalTestComponent as our component and configuring everything via the QuickConfigurationBuilder in our test case. This will amalgamate everything we discussed previously. public class MyTestCase extends TestCase implements EventCallback, FunctionalTestNotificationListener { QuickConfigurationBuilder builder; // this is where we create our configuration protected void setUp() throws Exception { builder = new QuickConfigurationBuilder(); // we create our MuleManager builder.createStartedManager(false, null); // we create a "SINGLE" endpoint and set the address to vm://single UMOEndpoint vmSingle = builder.createEndpoint("vm://Single","SingleEndpoint", true); // we create a FunctionalTestComponent and call it myComponent FunctionalTestComponent myComponent = new FunctionalTestComponent(); // we set out Event Callback on our test class myComponent.setEventCallback(this); // we register our component instance. builder.registerComponentInstance(myComponent,"SINGLE",vmSingle.getEndpointURI()); // we register our listener which we called "SINGLE" builder.getManager().registerListener(this,"SINGLE"); } We tear down our configuration once the test case is run. protected void tearDown() throws Exception { builder.disposeCurrent(); } The eventReceived method is the implementation of the method exposed by the EventCallback interface. We simply set the returnMessage property on our FunctionalTestComponent to "Customized Return Message", which is the message that we will expect to be returned to our test case public void eventReceived(UMOEventContext context, Object Component) throws Exception { UMOMessage message = context.getMessage(); FunctionalTestComponent fc = (FunctionalTestComponent) Component; fc.setReturnMessage("Customized Return Message"); } The following is our test case, i.e. from where we are going to send our request public void testSingleComponent() throws Exception { MuleClient client = new MuleClient(); // we send a message on the endpoint we created, i.e. vm://Single UMOMessage result = client.send("vm://Single", "hello", null); assertNotNull(result); assertEquals("Customized Return Message", result.getPayloadAsString()); } The onNotification method where we assert that the notification received was the one fired by the FunctionalTestComponent public void onNotification(UMOServerNotification notification)
{
assertTrue(notification.getAction() == FunctionalTestNotification.EVENT_RECEIVED);
}
}
A Complete Example using the Mule Configuration fileUsing the FunctionalTestCase, we can run the same example as above but using the mule configuration file. So first, we need to configure the mule-config.xml file to add our FunctionalTestComponent. <mule-configuration id="Mule_Sample" version="1.0"> <mule-environment-properties enableMessageEvents="true"/> <model name="myTest Model"> <mule-descriptor name="myComponent" implementation=" org.mule.tck.functional.FunctionalTestComponent"> <inbound-router> <endpoint address="vm://Single"/> </inbound-router> </mule-descriptor> </model> </mule-configuration> Next we need to build a Test Case that extends the FunctionalTestCase. Since, unlike the test with the QuickConfigurationBuilder, we do not create the component ourselves but is created for us through the configuration, we need to get an instance of that component, i.e. myComponent. To do this we need to use the MuleManager and retrieve from it the instance of the component from the model as seen in the snippet of code below. FunctionalTestComponent myComponent = (FunctionalTestComponent)MuleManager.getInstance().getModel().getComponent("myComponent").getInstance();
Once we have the instance we have to create an eventCallback and set it on the component. Note that we do not have to configure the setUp method since the setup will be done automatically. However, you can implement the doPreFunctionalSetUp() method if you need to add some additional things set up in your configuration. public class MyTestCase extends FunctionalTestCase { public void testSingleComponent() throws Exception { FunctionalTestComponent myComponent = (FunctionalTestComponent)MuleManager._getInstance_().getModel().getComponent("myComponent").getInstance(); // Create Callback EventCallback callback = new EventCallback() { public void eventReceived(UMOEventContext context, Object o) throws Exception { UMOMessage message = context.getMessage(); FunctionalTestComponent fc = (FunctionalTestComponent) o; fc.setReturnMessage("Customised Return Message"); } }; // set callback myComponent.setEventCallback(callback); // send the request MuleClient client = new MuleClient(); UMOMessage message = client.send("vm://test","hello", null); } We have to give the name of our configuration file to the method getConfigResources. We need to implement this method when extending the FunctionalTestCase. protected String getConfigResources() { return "mule-config.xml"; } } Using the eventCallback with a bean configured in Spring:When our mule-descriptor refers to a component whose implementation refers to a bean configured in Spring, getting the instance of the bean and setting the eventCallback is slightly different. Imagine we have our FunctionalTestComponent configured as a bean in Spring... <bean id="FunctionalTestBean" class="org.mule.tck.functional.FunctionalTestComponent">
If we are configuring Mule with the configuration file then we would have to use the Mule Manager to get an instance of the bean from the ContainerContext. FunctionalTestComponent myComponent = (FunctionalTestComponent)MuleManager.getInstance().getContainerContext().getComponent("FunctionalTestBean");
Alternatively, if we are using the QuickConfigurationBuilder to configure Mule we would get the Mule Manager through the builder in the following way. FunctionalTestComponent myComponent = (FunctionalTestComponent)builder.getManager().getContainerContext().getComponent("FunctionalTestBean");
Once we have retrieved the instance of the bean, we create an eventCallback and set it on the bean. EventCallback callback = new EventCallback() { public void eventReceived(UMOEventContext context, Object o) throws Exception { UMOMessage message = context.getMessage(); FunctionalTestComponent fc = (FunctionalTestComponent) o; fc.setReturnMessage("Customised Return Message"); } }; myComponent.setEventCallback(callback); A Complete Example Using the QuickConfigurationBuilder to get the instance of a Spring beanThe following is a complete example of how we would retrieve an instance of a spring bean from the springcontainer using the QuickConfigurationBuilder style of configuring Mule. public class MyTestCase extends TestCase implements FunctionalTestNotificationListener { QuickConfigurationBuilder builder; // this is the set-up of our configuration protected void setUp() throws Exception { builder = new QuickConfigurationBuilder(); builder.createStartedManager(false, null); //We need to create a SpringContainerContext and load the Spring configuration file in it. SpringContainerContext ctx =new SpringContainerContext(); ctx.initialise(); ctx.setConfigFile("spring-context-config.xml"); builder.setContainerContext(ctx); //We need to get an instance of the bean we have configured in Spring and set the Callback on it. FunctionalTestComponent myComponent = (FunctionalTestComponent)builder.getManager().getContainerContext().getComponent("FunctionalTestBean"); EventCallback callback = new EventCallback() { public void eventReceived(UMOEventContext context, Object o) throws Exception { UMOMessage message = context.getMessage(); FunctionalTestComponent fc = (FunctionalTestComponent) o; fc.setReturnMessage("Customised Return Message"); } }; myComponent.setEventCallback(callback); // Now we can continue to configure mule normally by adding the endpoints and registering the listener, etc. UMOEndpoint vmSingle = builder.createEndpoint("vm://Single","SingleEndpoint", true); builder.registerComponentInstance(myComponent, "SINGLE",vmSingle.getEndpointURI()); builder.getManager().registerListener(this,"SINGLE"); } This is our normal teardown protected void tearDown() throws Exception { builder.disposeCurrent(); } Now we proceed to set our test case. The FunctionalTestComponent should return the message "Customized Return Message". public void testSingleComponent() throws Exception { MuleClient client = new MuleClient(); UMOMessage result = client.send("vm://Single", "", null); assertNotNull(result); // expect the returning message to be Customised Return Message assertEquals("Customised Return Message", result.getPayloadAsString()); } This is the notification of events that will listen for the notification fired by the FunctionalTestComponent. public void onNotification(UMOServerNotification notification)
{
assertTrue(notification.getAction() == FunctionalTestNotification.EVENT_RECEIVED);
}
}
Note: The Spring configuration file is still used to configure the beans. <beans> <bean id="FunctionalTestBean" class="org.mule.tck.functional.FunctionalTestComponent"/> </beans> A Complete Example using the FunctionalTestCase with the Mule Configuration File to get the instance of a Spring beanUsing the FunctionalTestCase, we can run the same example as above but using the mule configuration file. So first, we need to configure the mule-config.xml file, with our component referring to the bean we have configured in Spring (FunctionalTestBean). Note: We will be using the same Spring bean we configured in the previous example. <mule-configuration id="Mule_Sample" version="1.0"> <mule-environment-properties enableMessageEvents="true"/> <container-context className="org.mule.extras.spring.SpringContainerContext"> <properties> <property name="configFile" value="spring-context-config.xml"/> </properties> </container-context> <model name="test"> <mule-descriptor name="myComponent" implementation="FunctionalTestBean"> <inbound-router> <endpoint address="vm://test"/> </inbound-router> </mule-descriptor> </model> </mule-configuration> Next we need to build a Test Case that extends the FunctionalTestCase. We should get the instance of the component from the Container Context. FunctionalTestComponent myComponent = (FunctionalTestComponent)MuleManager._getInstance_().getContainerContext().getComponent("FunctionalTestBean");
As usual, once we get the instance of the bean, we can create an eventCallback and set it on the component. public class myTestCase extends FunctionalTestCase{ public void testIDontKnow() throws Exception { FunctionalTestComponent myComponent = (FunctionalTestComponent)MuleManager.getInstance().getContainerContext().getComponent("FunctionalTestBean"); EventCallback callback = new EventCallback() { public void eventReceived(UMOEventContext context, Object o) throws Exception { UMOMessage message = context.getMessage(); FunctionalTestComponent fc = (FunctionalTestComponent) o; fc.setReturnMessage("Customised Return Message"); } }; myComponent.setEventCallback(callback); MuleClient client = new MuleClient(); UMOMessage message = client.send("vm://Single","hello", null); } protected String getConfigResources() { return "mule-config.xml"; } } |
Document generated by Confluence on Nov 27, 2006 10:27 |