Enterprise Integration Zone is brought to you in partnership with:

Pascal is a senior JEE Developer and Architect at 4Synergy in The Netherlands. Pascal has been designing and building J2EE applications since 2001. He is particularly interested in Open Source toolstack (Mule, Spring Framework, JBoss) and technologies like Web Services, SOA and Cloud technologies. Specialties: JEE XML Web Services Mule ESB Maven Cloud Technology Pascal is a DZone MVB and is not an employee of DZone and has posted 55 posts at DZone. You can read more from them at their website. View Full User Profile

Citrus Testcase Example: CSV File Inbound -> XML HTTP Outbound

02.03.2013
| 2777 views |
  • submit to reddit
As promised I’m going to show a test case for which I have used Citrus to implement it. The case is like this:

In a predefined folder on a machine a character-separated file is placed. This file is picked up by our Mule ESB application. The content is transformed to XML and this XML document is posted to another application over HTTP. This application responds with XML document telling the document was processed correctly.

To test this interface with Citrus we have to perform the following steps:

  1. Copy the input test-file to the predefined folder so it is picked up by the test instance of our Mule ESB
  2. Our Mule ESB will poll a directory each second to check if there is placed a new file to process. Our test case starts by having Citrus putting a test file in the directory .

  3. Have Citrus listen on a predefined HTTP port to receive the XML message over HTTP
  4. Inside our Mule ESB the file is transformed to XML message and posted on a HTTP port to be processed by the ‘ORDER’ system. We have configured Citrus to listen on this specific HTTP port to pick up the XML message.

  5. Reply to the received message with a predefined XML message
  6. This steps mimics the ‘ORDER’ system as it would normally process the incoming XML message and respond with another XML message. In this case Citrus will send back a predefined XML message as response.


The citrus-config.xml for this test case can be found here. I will explain the content of the config file next:

<bean id="citrusJMSConnectionFactory"
         class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL" value="vm://localhost:61616" />
        <property name="userName" value="user"/>
        <property name="password" value="password"/>
</bean>

This part defines a ‘in-memory’ JMS provider to be used by Citrus. We will see later how we use it to check the incoming HTTP request (as described here in the Citrus reference guide).

<bean id="namespaceContextBuilder" class="com.consol.citrus.xml.namespace.NamespaceContextBuilder">
        <property name="namespaceMappings">
            <props>
                <prop key="ns1">http://www.pascalalma.net/invoice</prop>
            </props>
        </property>
</bean>

Here we define the namespaces that are used in the xml messages in our test. We will use this namespace in the test to check values of certain XML elements.

<bean name="pascalalmaIntegrationTests"
         class="com.consol.citrus.TestSuite">
        <property name="tasksBefore">
            <list>
                <bean class="com.consol.citrus.actions.ExecuteSQLAction">
                    <property name="dataSource" ref="rsDataSource"/>
                    <property name="statements">
                        <list>
                            <value>DELETE FROM RS_LOGS</value>
                        </list>
                    </property>
                </bean>
            </list>
        </property>
</bean>

With this part I have Citrus clean up my log table in the database of my Mule ESB. We log all incoming and outgoing messages in our ESB to the database.

 
<!-- manadatory if XML files are used in a test -->
<bean id="xmlMessageValidator" class="com.consol.citrus.validation.xml.DomXmlMessageValidator"/>
    <bean id="schemaRepository"
           class="com.consol.citrus.xml.XsdSchemaRepository">
        <property name="schemas">
            <list>
                <bean class="org.springframework.xml.xsd.SimpleXsdSchema">
                    <property name="xsd"
                               value="classpath:schemas/rs_invoice_0.3.xsd"/>
                </bean>
            </list>
        </property>
</bean>

When you want to work with XML messages in your test (and that is very likely nowadays) you will need to define an ‘xmlMessageValidator’. And to be able to validate the message against an XML schema you will need to define the schemas in the ‘schemaRepository’ as I have done here.

<bean id="rsDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="url" value="jdbc:oracle:thin:@test-vm:1521:XE"/>
        <property name="username" value="user"/>
        <property name="password" value="password"/>
        <property name="driverClassName" value="oracle.jdbc.xa.client.OracleXADataSource"/>
        <property name="removeAbandoned" value="true"/>
</bean> 

This is the datasource that Citrus can use to access the database that is used by our Mule ESB application. We use this datasource to clean up the logtable and can use it to perform validations after messages have been processed.

 <citrus:message-channel-sender id="invoiceFileSender" channel="fileOutboundChannel"/>
<file:outbound-channel-adapter id="invoice-files-out" channel="fileOutboundChannel"
        directory="file://Volumes/staging/invoice" filename-generator="test-filename-generator" />
<bean id="test-filename-generator" class="net.pascalalma.citrus.TestFileNameGenerator" />
<si:channel id="fileOutboundChannel" />

This is a mix of Citrus with spring-integration configuration. With this configuration I am able to send files to my test VM with Citrus. It also shows how easy it is to mix Citrus with spring-integration.

<!-- receive Invoice HTTP request -->
<citrus-http:server id="orderHttpServer"
               port="8060"
               uri="/services/post-invoice"
               deamon="false"
               message-handler="jmsForwardingMessageHandler"
               auto-start="true"/>

Definition of the HTTP Server in Citrus which listens on port 8060 for a request of our Mule ESB.

<bean id="jmsForwardingMessageHandler"
        class="com.consol.citrus.adapter.handler.JmsConnectingMessageHandler">
       <property name="destinationName" value="Citrus.order-response"/>
       <property name="connectionFactory" ref="citrusJMSConnectionFactory" />
       <property name="replyTimeout" value="6000"/>
   </bean>

   <!-- Process all received requests -->
   <citrus:jms-sync-message-receiver id="orderRequestReceiver"
                           destination-name="Citrus.order-response"
                           connection-factory="citrusJMSConnectionFactory" />
   <citrus:jms-reply-message-sender id="orderResponseSender"
                           reply-destination-holder="orderRequestReceiver"
                           connection-factory="citrusJMSConnectionFactory" />

This part forwards the incoming HTTP request to a JMS endpoint. In our test we can perform tests on this incoming JMS message and check if it is valid. Next thing I want to explain is the test-config.xml itself. I think it is quite self-explainatory:

<?xml version="1.0" encoding="UTF-8"?>
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:ws="http://www.citrusframework.org/schema/ws/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://www.springframework.org/schema/beans
              http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                  http://www.citrusframework.org/schema/ws/testcase
                  http://www.citrusframework.org/schema/ws/testcase/citrus-ws-testcase.xsd
                  http://www.citrusframework.org/schema/testcase
                  http://www.citrusframework.org/schema/testcase/citrus-testcase.xsd">

    <testcase name="Testcase001Test">
        <meta-info>
            <author>Pascal Alma</author>
            <creationdate>2011-04-24</creationdate>
            <status>DRAFT</status>
        </meta-info>

        <description>Tests Testcase001</description>

        <actions>
            <send with="invoiceFileSender">
                <message>
                    <resource file="classpath:data/testcase001.txt"/>
                </message>
            </send>
            <receive with="orderRequestReceiver">

                <message schema-validation="true">
                    <validate path="//ns1:post-invoice/ns1:CompanyCode" value="1"/>
                    <validate path="string:count(//ns1:post-invoice/ns1:InvoiceLine/ns1:DocID)" value="3"/>
                </message>
                <header>
                    <element name="rs_address" value="order-purchase-invoice-queue" />
                </header>
                <extract>
                    <message path="count(//ns1:post-invoice/ns1:InvoiceLine/ns1:DocID)" variable="${nrOfInvoiceLines}"/>
                </extract>
            </receive>

            <echo>
                <message>${nrOfInvoiceLines}</message>
            </echo>
            <send with="orderResponseSender">
                <message>
                    <resource file="classpath:data/response-testcase001.xml"/>
                </message>
            </send>
        </actions>
    </testcase>
</spring:beans>

The first action that is done is sending a file to my Mule ESB. The next step is that I wait for the response from my ESB. When I received that I check the contents of XML message at some points and check if it is valid against the schema.

Then after echoing the number of invoice lines I have Citrus return the HTTP request with a fixed XML response so the Mule ESB ends this action successfully.

That completes this example of how to use Citrus to test your ESB implemention. I hope it is more clear now how you can use Citrus for your (automated) integration tests with this real life example.

Published at DZone with permission of Pascal Alma, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)