Copyright © 2018 ConSol Software GmbH
Version: 2.7.4

citrus-logo

1. Preface

Integration testing can be very hard, especially when there is no sufficient tool support. Unit testing is flavored with fantastic tools and APIs like JUnit, TestNG, EasyMock, Mockito and so on. These tools support you in writing automated tests. A tester who is in charge of integration testing may lack of tool support for automated testing especially when it comes to simulate messaging interfaces.

In a typical enterprise application scenario the test team has to deal with different messaging interfaces and various transport protocols. Without sufficient tool support the automated integration testing of message-based interactions between interface partners is exhausting and sometimes barely possible.

The tester is forced to simulate several interface partners in an end-to-end integration test. The first thing that comes to our mind is manual testing. No doubt manual testing is fast. In long term perspective manual testing is time consuming and causes severe problems regarding maintainability as they are error prone and not repeatable.

The Citrus framework gives a complete test automation tool for integration testing of enterprise applications. You can test your message interfaces to other applications as client and server. Every time a code change applies all automated Citrus tests ensure the stability of interfaces and message communication.

Regression testing and continuous integration is very easy as Citrus fits into your build lifecycle as usual Java unit test. You can use Citrus with JUnit or TestNG in order to integrate with your application build.

With powerful validation capabilities for various message formats like XML, CSV or JSON Citrus is designed to provide fully automated integration tests for end-to-end use cases. Citrus effectively composes complex messaging use cases with response generation, error simulation, database interaction and more.

This documentation provides a reference guide to all features of the Citrus test framework. It gives a detailed picture of effective integration testing with automated integration test environments. Since this document is considered to be under construction, please do not hesitate to give any comments or requests to us using our user or support mailing lists.

2. What’s new in Citrus 2.7?!

Citrus 2.7 is using Java 8! The Citrus sources are compiled with Java 8 which means that from now on you need at least Java 8 runtime to work with Citrus. With this Java 8 base Citrus is proud to welcome two new crew members for supporting Selenium and Kubernetes in tests. Not enough we have the following features included in the box.

2.1. Since Citrus 2.7.4

2.1.1. JDBC server

Preparing databases for testing can be hard times. Creating all tables and preparing the test data with all constraints and data integrity is often a full time job and very exhausting. Instead of preparing a real database would’nt it be nice to just mock the database queries with proper result set generation just in time within the test? But at the same time we need to really use JDBC to connect and retrieve the data from a JDBC mock server.

This is now possible with the new JDBC server integration in Citrus. You can receive incoming SQL statements (INSERT, UPDATE, SELECT, DELETE, …​) and respond with a proper data set and/or rows updated result. This enables us to test the data access in a database persistence layer without having to actually create the tables and data needed for the test scenario.

Read about it in chapter JDBC server.

2.1.2. Async container

Sometimes it is good to execute test actions in parallel so you can do things simultaneously in a test case. In some cases it is just to execute a single test action in parallel to the rest of the test. When using send operations you already could have used fork="true" option on that test action. The async test action container provides such functionality for all other test actions, too. Just add a test action to the async container and the action is executed in a separate thread. The test case is not blocked with that action execution and immediately executes the next action in place.

Read about it in chapter Async.

2.1.3. System/Env property functions

There are new functions available to access System properties and environment settings. This enables you to resolve property values in test cases at runtime. See how to use this functions in chapter functions.

2.1.4. URL encode/decode functions

Two new functions enable you to URL encode/decode a String with proper URL escaping. See how to use this functions in chapter functions.

2.2. Since Citrus 2.7.3

2.2.1. Ignore sections in plain text

Plain text message validation is usually based on a complete String equals comparison. With latest release we added the possibility to ignore some sections with well known @ignore@ keyword placeholder. The message validator will automatically ignore words or character sections based on that. Read more about this in chapter plain text message validation.

Also possible is the extraction of sections as new test variables when using the @variable()@ matcher in the plain text message content.

2.2.2. Json schema validation

When dealing with Json message content the latest release allows adding of schema validation. The Json structure is validated with proper schema as of Open API (Swagger) schema rules. As usual the available schema files are defined in a schema repository in the project configuration. Read more about this in chapter json schema validation.

2.2.3. JUnit5 support

With this release you are able to integrate Citrus with JUnit5 the new generation of the famous unit testing framework. We provide a Citrus JUnit5 extension that can do the trick. Read more about this in chapter run with JUnit5.

2.3. Since Citrus 2.7.2

2.3.1. Database transaction handling

When Citrus accesses data storage in form of SQL statements executed on some datasource the transaction handling has not been set in the past. Each SQL statement has been committed immediately. Especially when executing multiple SQL statement via script this could lead to inconsistencies. With the new release you can make use of Spring’s transaction handling when executing SQL statements with Citrus. You can set a transaction handler with isolation levels and default transaction timeout settings. This enables you to use transaction blocks for multiple statements with one single commit or rollback. Read more about this in chapter actions accessing the database.

2.3.2. Environment settings

We added a mechanism to overwrite general settings in Citrus via system properties and environment variables. This makes Citrus ready for runtime environments such as Docker and Kubernetes where you can use environment variables to change Citrus behavior. The available settings and variable names can be seen in chapter configuration.

2.3.3. Http cookie support

Setting Http cookie related Http headers has been possible in previous versions. We improved that cookie handling in Http request and response messages with a dedicated DSL for adding and verifying cookie information in Http headers. The Citrus http-server is able to advice the client to set a new cookie with respective Set-Cookie headers in response messages. The http-client is able to verify the cookie attributes such as name, value, max-age and so on. In addition to that the client is able to send the cookie name value pair in further requests as a reference via "Cookie" message headers. The complete new cookie handling is described in section Http cookie handling.

2.3.4. File resource encoding

The Citrus test case is able to load file resources in various situations e.g. when defining expected message contents. The file resource encoding and charset used a system-wide default setting which has been settable via system property (citrus.file.encoding). In addition to that we have added the opportunity to overwrite this system-wide setting in each resource read operation. This means that everywhere where it is possible to load a file resource you can explicitly set a file encoding and charset for this read operation. This enables you to use more than one system-wide file encoding setting when reading data from external file resources in Citrus.

2.4. Since Citrus 2.7

2.4.1. Java 8

Citrus is now using Java 8. This is mainly because we need to move on in using latest versions of Spring Framework, Apache Camel and so on. If you are still stuck on Java 7 you can not update to 2.7 as the Citrus sources are compiled with Java 8. Please contact us in case you really can not update to Java 8 in your project. We can think of a minor bugfix version with Citrus 2.6 base that still supports Java 7 runtime. On the bright side we can now use the full power of Lambda expressions and other Java 8 features in Citrus code base.

2.4.2. Kubernetes support

Citrus is now able to interact with Kubernetes remote API in order to manage pods, services and other resources on the Kubernetes platform. The Kubernetes client is based on the Fabric8 Java client that interacts with the Kubernetes API via REST services. So you can access Kubernetes resources within Citrus in order to change or validate the resource state for containerized testing. This is very useful when dealing with container application environments as part of the integration tests. Please stay tuned for blog posts and tutorial samples on how Citrus can help you test Microservices with Docker and Kubernetes. The basic usage is described in section kubernetes.

2.4.3. Selenium support

User interface and browser testing has not been a focus within Citrus integration testing until now that we can integrate with the famous Selenium UI testing library. Thanks to the great contributions made by the community - especially by vdsrd@github - we can use Selenium based actions and features directly in a Citrus test case. The Citrus Java and XML DSL both provide comfortable access to the Selenium API in order to simulate user interaction within a browser. The mix of user based actions and Citrus messaging transport simulation gives complete new ways of handling complex integration scenarios. Read more about this in chapter Selenium.

2.4.4. Environment based before/after suite

You can enable/disable before and after suite actions based on optional environment or system properties. Users can give property names or property values that are checked before execution. Only in case the environment property checks do pass the actions are executed before/after the test suite run.

2.4.5. WsAddressing header customization

We have improved the header customization options when using SOAP WSAddressing feature. You can now overwrite the default WSAddressing headers per test action in addition to defining the headers on client endpoint component level.

2.4.6. JsonPath data dictionary

Json data dictionary was based on a simple dot notated syntax. Now you can also use more complex JsonPath expressions in order to overwrite elements in Json messages based on the data dictionary settings in Citrus. Read more about that in chapter data-dictionary.

2.4.7. Java DSL test behavior

Test behaviors in Java DSL represent templates in XML DSL. The behavior encapsulates a set of test actions to a group that can be applied to multiple Java DSL tests. This enables you to combine common test actions in Java DSL with more comfortable reuse of test action definitions. See chapter test-behavior how to use that.

2.4.8. Auto select message type

Default message type for validation tasks in Citrus has been XML. Based on this message type the respective message validator implementation applies for XML, JSON, plain text and so on. You can now change this default message type by setting a system property (citrus.default.message.type). Also Citrus improved the auto select algorithm when the default message type is obviously not applicable. When a message arrives in Citrus the receiving action tries to find out which message validator fits best according to the message payload. XML message content is automatically identified by <> characters. JSON message payloads are identified by {} or [] characters for objects and array representations. This way Citrus tries to find the best matching message validator for the incoming message. Before that Citrus has always been using the default message type XML.

Read about different message validators in message-validation.

2.4.9. Default Cucumber steps

The Citrus Cucumber extension now defines default step definitions for Http, Docker and Selenium. These default steps are ready for usage in any Cucumber Citrus feature specification. You can load the default steps as additional glue packages in your Cucumber options. After that you are ready to go for using the default steps directly in feature specification files. With the extensions you can perform Docker and Selenium commands very easy. Also you can describe the Http REST client-server communication in BDD style. Read more about this in cucumber.

2.4.10. Refactoring

Deprecated APIs and classes that coexisted a long time are now removed. If your project is using on of these deprecated classes you may run into compile time errors. Please have a look at the Citrus API JavaDocs and documentation in order to find out how to use the new APIs and classes that replaced the old deprecated stuff.

2.5. Bugfixes

Bugs are part of our software developers world and fixing them is part of your daily business, too. Finding and solving issues makes Citrus better every day. For a detailed listing of all bugfixes please refer to the complete changes log of each release.

3. Introduction

Nowadays enterprise applications usually communicate with different partners over loosely coupled messaging interfaces. The interaction and the interface contract needs to be tested in integration testing.

In a typical integration test scenario we need to simulate the communication partners over various transports. How can we test use case scenarios that include several interface partners interacting with each other? How can somebody ensure that the software components work correctly regarding the interface contract? How can somebody run integration test cases in an automated reproducible way? Citrus tries to answer these questions!

3.1. Overview

Citrus aims to strongly support you in simulating interface partners across different messaging transports. You can easily produce and consume messages with a wide range of protocols like HTTP, JMS, TCP/IP, FTP, SMTP and more. The framework is able to both act as a client and server. In each communication step Citrus is able to validate message contents towards syntax and semantics.

In addition to that the Citrus offers a wide range of test actions to take control of the process flow during a test (e.g. iterations, system availability checks, database connectivity, parallelism, delaying, error simulation, scripting and many more).

The basic goal in Citrus test cases is to describe a whole use case scenario including several interface partners that exchange many messages with each other. The composition of complex message flows in a single test case with several test steps is one of the major features in Citrus.

The test case description is either done in XML or Java and can be executed multiple times as automated integration test. With JUnit and TestNG integration Citrus can easily be integrated into your build lifecycle process. During a test Citrus simulates all surrounding interface partners (client or server) without any coding effort. With easy definition of expected message content (header and payload) for XML, CSV, SOAP, JSON or plaintext messages Citrus is able to validate the incoming data towards syntax and semantics.

3.2. Usage scenarios

If you are in charge of an enterprise application in a message based solution with message interfaces to other software components you should use Citrus. In case your project interacts with other software over different messaging transports and in case you need to simulate these interface partners on client or server side you should use Citrus. In case you need to continuously check the software stability not only on a unit testing basis but also in an end-to-end integration scenario you should use Citrus. Bug fixing, release or regression testing is very easy with Citrus. In case you are struggling with code stability and feel uncomfortable regarding your next software release you should definitely use Citrus.

usage_sample.jpg

This test set up is typical for a Citrus use case. In such a test scenario we have a system under test (SUT) with several message interfaces to other applications like you would have with an enterprise service bus for instance. A client application invokes services on the SUT application. The SUT is linked to several backend applications over various messaging transports (here SOAP, JMS, and Http). Interim message notifications and final responses are sent back to the client application. This generates a bunch of messages that are exchanged throughout the applications involved.

In the automated integration test Citrus needs to send and receive those messages over different transports. Citrus takes care of all interface partners (ClientApplication, Backend1, Backend2, Backend3) and simulates their behavior by sending proper response messages in order to keep the message flow alive.

Each communication step comes with message validation and comparison against an expected message template (e.g. XML or JSON data). Besides messaging actions Citrus is also able to perform arbitrary other test actions. Citrus is able to perform a database query between requests as an example.

The Citrus test case runs fully automated as a Java application. In fact a Citrus test case is nothing but a JUnit or TestNG test case. Step by step the whole use case scenario is performed like in a real production environment. The Citrus test is repeatable and is included into the software build process (e.g. using Maven or ANT) like a normal unit test case would do. This gives you fully automated integration tests to ensure interface stability.

The following reference guide walks through all Citrus capabilities and shows how to set up a great integration test with Citrus.

4. Setup

This chapter discusses how to get started with Citrus. It deals with the installation and set up of the framework, so you are ready to start writing test cases after reading this chapter.

Usually you would use Citrus as a dependency library in your project. In Maven you would just add Citrus as a test-scoped dependency in your POM. When using ANT you can also run Citrus as normal Java application from your build.xml. As Citrus tests are nothing but normal unit tests you could also use JUnit or TestNG ant tasks to execute the Citrus test cases.

This chapter describes the Citrus project setup possibilities, choose one of them that fits best to include Citrus into your project.

4.1. Using Maven

Citrus uses Maven internally as a project build tool and provides extended support for Maven projects. Maven will ease up your life as it manages project dependencies and provides extended build life cycles and conventions for compiling, testing, packaging and installing your Java project. Therefore it is recommended to use the Citrus Maven project setup. In case you already use Maven it is most suitable for you to include Citrus as a test-scoped dependency.

As Maven handles all project dependencies automatically you do not need to download any Citrus project artifacts in advance. If you are new to Maven please refer to the official Maven documentation to find out how to set up a Maven project.

4.1.1. Maven archetype

If you start from scratch or in case you would like to have Citrus operating in a separate Maven module you can use the Citrus Maven archetype to create a new Maven project. The archetype will setup a basic Citrus project structure with basic settings and files.

mvn archetype:generate -Dfilter=com.consol.citrus.mvn:citrus

1: remote -> com.consol.citrus.mvn:citrus-quickstart (Citrus quickstart project)
2: remote -> com.consol.citrus.mvn:citrus-quickstart-jms (Citrus quickstart project with JMS consumer and producer)
3: remote -> com.consol.citrus.mvn:citrus-quickstart-soap (Citrus quickstart project with SOAP client and producer)
Choose a number: 1

Define value for groupId: com.consol.citrus.samples
Define value for artifactId: citrus-sample
Define value for version: 1.0-SNAPSHOT
Define value for package: com.consol.citrus.samples

In the sample above we used the Citrus archetype available in Maven central repository. As the list of default archetypes available in Maven central is very long, it has been filtered for official Citrus archetypes.

After choosing the Citrus quickstart archetype you have to define several values for your project: the groupId, the artifactId, the package and the project version. After that we are done! Maven created a Citrus project structure for us which is ready for testing. You should see the following basic project folder structure.

citrus-sample
  |   + src
  |   |   + main
  |   |    |   + java
  |   |    |   + resources
  |   |   + citrus
  |   |    |   + java
  |   |    |   + resources
  |   |    |   + tests
  pom.xml

The Citrus project is absolutely ready for testing. With Maven we can build, package, install and test our project right away without any adjustments. Try to execute the following commands:

mvn integration-test
mvn integration-test -Dtest=MyFirstCitrusTest
If you need additional assistance in setting up a Citrus Maven project please visit our Maven setup tutorial on http://www.citrusframework.org/tutorials.html.

4.1.2. Existing Maven projects

In case you already have a proper Maven project you can also integrate Citrus with it. Just add the Citrus project dependencies in your Maven pom.xml as a dependency like follows.

  • We add Citrus as test-scoped project dependency to the project POM (pom.xml)

<dependency>
  <groupId>com.consol.citrus</groupId>
  <artifactId>citrus-core</artifactId>
  <version>2.7.4</version>
  <scope>test</scope>
</dependency>
  • In case you would like to use the Citrus Java DSL also add this dependency to the project

<dependency>
  <groupId>com.consol.citrus</groupId>
  <artifactId>citrus-java-dsl</artifactId>
  <version>2.7.4</version>
  <scope>test</scope>
</dependency>
  • Add the citrus Maven plugin to your project

<plugin>
  <groupId>com.consol.citrus.mvn</groupId>
  <artifactId>citrus-maven-plugin</artifactId>
  <version>2.7.4</version>
  <configuration>
    <author>Donald Duck</author>
    <targetPackage>com.consol.citrus</targetPackage>
  </configuration>
</plugin>

Now that we have added Citrus to our Maven project we can start writing new test cases with the Citrus Maven plugin:

mvn citrus:create-test

Once you have written the Citrus test cases you can execute them automatically in your Maven software build lifecycle. The tests will be included into your projects integration-test phase using the Maven failsafe plugin. Here is a sample failsafe configuration for Citrus.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-failsafe-plugin</artifactId>
  <version>2.20</version>
  <executions>
    <execution>
      <id>integration-tests</id>
      <goals>
        <goal>integration-test</goal>
        <goal>verify</goal>
      </goals>
    </execution>
  </executions>
 </plugin>

The Citrus test sources go to the default Maven test sources directory src/test/java and src/test/resources:

Now everything is set up and you can call the usual Maven install goal (mvn clean install) in order to build your project. The Citrus integration tests are executed automatically during the build process. Besides that you can call the Maven integration-test phase explicitly to execute all Citrus tests or a specific test by its name:

mvn integration-test
mvn integration-test -Dtest=MyFirstCitrusIT
The Maven failsafe plugin by default executed tests with specific name pattern. This is because integration tests should not execute in Maven unit test phase, too. Therefore integration tests should follow the failsafe name pattern with each test name beginning or ending with 'IT'.
If you need additional assistance in setting up a Citrus Maven project please visit our Maven setup tutorial on http://www.citrusframework.org/tutorials.html.

4.2. Using Gradle

As Citrus tests are nothing but normal JUnit or TestNG tests the integration to Gradle as build tool is as easy as adding the source files to a folder in your project. With the Gradle task execution for integration tests you are able to execute the Citrus tests like you would do with normal unit tests.

4.2.1. Configuration

The Gradle build configuration is done in the build.gradle and settings.gradle files. Here we define the project name and the project version.

rootProject.name = 'citrus-sample-gradle'
group 'com.consol.citrus.samples'
version '2.7.4'

Now as Citrus libraries are available on Maven central repository we add these repositories so Gradle knows how to download the required Citrus artifacts.

repositories {
    mavenCentral()
    maven {
        url 'http://labs.consol.de/maven/snapshots-repository/'
    }
}

Citrus stable release versions are available on Maven central. If you want to use the very latest next version as snapshot preview you need to add the ConSol Labs snapshot repository which is optional. Now lets move on with adding the Citrus libraries to the project.

dependencies {
    testCompile group: 'com.consol.citrus', name: 'citrus-core', version: '2.7.4'
    testCompile group: 'com.consol.citrus', name: 'citrus-java-dsl', version: '2.7.4'
    testCompile group: 'org.testng', name: 'testng', version: '6.11'
    [...]
}

This enables the Citrus support for the project so we can use the Citrus classes and APIs. We decided to use TestNG unit test library.

test {
    useTestNG()
}

Of course JUnit is also supported. This is all for build configuration settings. We can move on to writing some Citrus integration tests. You can find those tests in src/test/java directory.

4.2.2. Run with Gradle

You can use the Gradle wrapper for compile, package and test the sample with Gradle build command line.

 gradlew clean build

This executes all Citrus test cases during the build and you will see Citrus performing some integration test logging output. After the tests are finished build is successful and you are ready to go for writing some tests on your own.

If you just want to execute all tests you can call

gradlew clean check

Of course you can also start the Citrus tests from your favorite IDE. Just start the Citrus test using the Gradle integration in IntelliJ, Eclipse or Netbeans.

4.3. Using Ant

Ant is a very popular way to compile, test, package and execute Java projects. The Apache project has effectively become a standard in building Java projects. You can run Citrus test cases with Ant as Citrus is nothing but a Java application. This section describes the steps to setup a proper Citrus Ant project.

4.3.1. Preconditions

Before we start with the Citrus setup be sure to meet the following preconditions. The following software should be installed on your computer, in order to use the Citrus framework:

  • Java 8 or higher

Installed JDK plus JAVA_HOME environment variable set up and pointing to your Java installation directory

  • Java IDE (optional)

A Java IDE will help you to manage your Citrus project (e.g. creating and executing test cases). You can use the any Java IDE (e.g. Eclipse or IntelliJ IDEA) but also any convenient XML Editor to write new test cases.

  • Ant 1.8 or higher

Ant (http://ant.apache.org/) will run tests and compile your Citrus code extensions if necessary.

4.3.2. Download

First of all we need to download the latest Citrus release archive from the official website http://www.citrusframework.org

Citrus comes to you as a zipped archive in one of the following packages:

  • citrus-x.x-release

  • citrus-x.x-src

The release package includes the Citrus binaries as well as the reference documentation and some sample applications.

In case you want to get in touch with developing and debugging Citrus you can also go with the source archive which gives you the complete Citrus Java code sources. The whole Citrus project is also accessible for you on http://github.com/christophd/citrus. This open git repository on GitHub enables you to build Citrus from scratch with Maven and contribute code changes.

4.3.3. Installation

After downloading the Citrus archives we extract those into an appropriate location on the local storage. We are seeking for the Citrus project artifacts coming as normal Java archives (e.g. citrus-core.jar, citrus-ws.jar, etc.)

You have to include those Citrus Java archives as well as all dependency libraries to your Apache Ant Java classpath. Usually you would copy all libraries into your project’s lib directory and declare those libraries in the Ant build file. As this approach can be very time consuming I recommend to use a dependency management API such as Apache Ivy which gives you automatic dependency resolution like that from Maven. In particular this comes in handy with all the 3rd party dependencies that would be resolved automatically.

No matter what approach you are using to set up the Apache Ant classpath see the following sample Ant build script which uses the Citrus project artifacts in combination with the TestNG Ant tasks to run the tests.

<project name="citrus-sample" basedir="." default="citrus.run.tests" xmlns:artifact="antlib:org.apache.maven.artifact.ant">

  <property file="src/it/resources/citrus.properties"/>

  <path id="maven-ant-tasks.classpath" path="lib/maven-ant-tasks-2.1.3.jar" />
  <typedef resource="org/apache/maven/artifact/ant/antlib.xml"
    uri="antlib:org.apache.maven.artifact.ant"
    classpathref="maven-ant-tasks.classpath" />

  <artifact:pom id="citrus-pom" file="pom.xml" />
  <artifact:dependencies filesetId="citrus-dependencies" pomRefId="citrus-pom" />

  <path id="citrus-classpath">
    <pathelement path="src/it/java"/>
    <pathelement path="src/it/resources"/>
    <pathelement path="src/it/tests"/>
    <fileset refid="citrus-dependencies"/>
  </path>

  <taskdef resource="testngtasks" classpath="lib/testng-6.8.8.jar"/>

  <target name="compile.tests">
    <javac srcdir="src/it/java" classpathref="citrus-classpath"/>
    <javac srcdir="src/it/tests" classpathref="citrus-classpath"/>
  </target>

  <target name="create.test" description="Creates a new empty test case">
    <input message="Enter test name:" addproperty="test.name"/>
    <input message="Enter test description:" addproperty="test.description"/>
    <input message="Enter author's name:" addproperty="test.author" defaultvalue="${default.test.author}"/>
    <input message="Enter package:" addproperty="test.package" defaultvalue="${default.test.package}"/>
    <input message="Enter framework:" addproperty="test.framework" defaultvalue="testng"/>

    <java classname="com.consol.citrus.util.TestCaseCreator">
      <classpath refid="citrus-classpath"/>
      <arg line="-name ${test.name} -author ${test.author} -description ${test.description} -package ${test.package} -framework ${test.framework}"/>
    </java>
  </target>

  <target name="citrus.run.tests" depends="compile.tests" description="Runs all Citrus tests">
    <testng classpathref="citrus-classpath">
      <classfileset dir="src/it/java" includes="**/*.class" />
    </testng>
  </target>

  <target name="citrus.run.single.test" depends="compile.tests" description="Runs a single test by name">
    <touch file="test.history"/>
    <loadproperties srcfile="test.history"/>

    <echo message="Last test executed: ${last.test.executed}"/>
    <input message="Enter test name or leave empty for last test executed:" addproperty="testclass" defaultvalue="${last.test.executed}"/>

    <propertyfile file="test.history">
      <entry key="last.test.executed" type="string" value="${testclass}"/>
    </propertyfile>

    <testng classpathref="citrus-classpath">
      <classfileset dir="src/it/java" includes="**/${testclass}.class" />
    </testng>
  </target>

</project>
If you need detailed assistance for building Citrus with Ant do also visit our tutorials section on http://www.citrusframework.org. There you can find a tutorial which describes the Citrus Java project set up with Ant from scratch.

5. Test cases

Now let us start writing test cases! A test case in Citrus describes all steps for a certain use case in one single file. The Citrus test holds a sequence of test actions. Each action represents a very special purpose such as sending or receiving a message. Typically with message-based enterprise applications the sending and receiving of messages represent the main actions inside a test.

However you will learn that Citrus is more than just a simple SOAP client for instance. Each test case can hold complex actions such as connecting to the database, transforming data, adding loops and conditional steps. With the default Citrus action set you can accomplish very complex use case integration tests. Later in this guide we will briefly discuss all available test actions and learn how to use various message transports within the test. For now we will concentrate on the basic test case structure.

figure_004.jpg

The figure above describes a typical test action sequence in Citrus. A list of sending and receiving test actions composing a typical test case here. Each action references a predefined Citrus endpoint component that we are going to talk about later on.

So how do we define those test cases? In general Citrus specifies test cases as Java classes. With TestNG or JUnit you can execute the Citrus tests within your Java runtime as you would do within unit testing. You can code the Citrus test in a single Java class doing assertions and using Spring’s dependency injection mechanisms.

If you are not familiar to writing Java code you can also write Citrus tests as XML files. Whatever test language you choose for Citrus the whole test case description takes place in one single file (Java or XML). This chapter will introduce the custom XML schema language as well as the Java domain specific language so you will be able to write Citrus test cases no matter what knowledge base you belong to.

5.1. Writing test cases in XML

Put simply, a Citrus test case is nothing but a simple Spring XML configuration file. The Spring framework has become a state of the art development framework for enterprise Java applications. As you work with Citrus you will also learn how to use the Spring Ioc (Inversion of control) container and the concepts of dependency injection. So let us have a look at the pure Spring XML configuration syntax first. You are free to write fully compatible test cases for the Citrus framework just using this syntax.

Spring bean definition syntax
<beans
    xmlns="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.xsd">

    <bean name="MyFirstTest"
             class="com.consol.citrus.TestCase">
        <property name="variableDefinitions">
            <!-- variables of this test go here -->
        </property>
        <property name="actions">
            <!-- actions of this test go here -->
        </property>
    </bean>
</beans>

Citrus can execute these Spring bean definitions as normal test cases - no problem, but the pure Spring XML syntax is very verbose and probably not the best way to describe a test case in Citrus. In particular you have to know a lot of Citrus internals such as Java class names and property names. In addition to that as test scenarios get more complex the test cases grow in size. So we need a more effective and comfortable way of writing tests. Therefore Citrus provides a custom XML schema definition for writing test cases which is much more adequate for our testing purpose.

The custom XML schema aims to reach the convenience of domain specific languages (DSL). Let us have a look at the Citrus test describing XML language by introducing a first very simple test case definition:

XML DSL
<spring:beans
    xmlns="http://www.citrusframework.org/schema/testcase"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:spring="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.citrusframework.org/schema/testcase
    http://www.citrusframework.org/schema/testcase/citrus-testcase.xsd">

    <testcase name="MyFirstTest">
        <description>
            First example showing the basic test case definition elements!
        </description>
        <variables>
            <variable name="text" value="Hello Test Framework"/>
        </variables>
        <actions>
            <echo>
                <message>${text}</message>
            </echo>
        </actions>
    </testcase>
</spring:beans>

We do need the `<spring:beans>` root element as the XML file is read by the Spring IoC container. Inside this root element the Citrus specific namespace definitions take place.

The test case itself gets a mandatory name that must be unique throughout all test cases in a project. You will receive errors when using duplicate test names. The test name has to follow the common Java naming conventions and rules for Java classes. This means names must not contain any whitespace characters but characters like '-', '.', '' are supported. For example, _TestFeature_1 is valid but Test Feature 1 is not as it contains whitespace characters like spaces.

Now that we have an XML definition that describes the steps of our test we need a Java executable for the test. The Java executable is needed for the framework in order to run the test. See the following sample Java class that represents a simple Citrus Java test:

import org.testng.annotations.Test;
import com.consol.citrus.annotations.CitrusTest;
import com.consol.citrus.testng.AbstractTestNGCitrusTest;

@Test
public class MyFirstTest extends AbstractTestNGCitrusTest {

    @CitrusXmlTest(name = "MyFirstTest")
    public void myFirstTest() {
    }
}

The sample above is a Java class that represents a valid Citrus Java executable. The Java class has no programming logic as we use a XML test case here. The Java class can also be generated using the Citrus Maven plugin. The Java class extends from basic superclass AbstractTestNGCitrusTest and therefore uses TestNG as unit test framework. Citrus also supports JUnit as unit test framework. Read more about this in run-with-testngand run-with-junit.

Up to now it is important to understand that Citrus always needs a Java executable test class. In case we use the XML test representation the Java part is generic, can be generated and contains no programming logic. The XML test defines all steps and is our primary test case definition.

5.2. Writing test cases in Java

Before we go into more details on the attributes and actions that take place within a test case we just have a look at how to write test cases with pure Java code. Citrus works with Java and uses the well known JUnit and TestNG framework benefits that you may be used to as a tester. Many users may prefer to write Java code instead of the verbose XML syntax. Therefore you have another possibility for writing Citrus tests in pure Java.

When using the Citrus Java DSL we need to include a special Maven dependency module to our project that provides the needed API.

<dependency>
  <groupId>com.consol.citrus</groupId>
  <artifactId>citrus-java-dsl</artifactId>
  <version>2.7.4</version>
  <scope>test</scope>
</dependency>

Citrus in general differences between two ways of test cases in Java. These are test-designers and test-runners that we deal with each in the next two sections.

5.3. Java DSL test designer

The first way of defining a Citrus test in Java is the test-designer . The Java DSL for a test designer works similar to the XML approach. The whole test case is built with all test actions first. Then the whole test case is executed as a whole Citrus test. This is how to define a Citrus test with designer Java DSL methods:

Java DSL designer
import org.testng.annotations.Test;
import com.consol.citrus.annotations.CitrusTest;
import com.consol.citrus.dsl.testng.TestNGCitrusTestDesigner;

@Test
public class MyFirstTestDesigner extends TestNGCitrusTestDesigner {
    @CitrusTest(name = "MyFirstTest")
    public void myFirstTest() {
        description("First example showing the basic test case definition elements!");

        variable("text", "Hello Test Framework");

        echo("${text}");
    }
}

Citrus provides a base Java class com.consol.citrus.dsl.testng.TestNGCitrusTestDesigner that provides all capabilities for you in form of builder pattern methods. Just use the @CitrusTest annotation on top of the test method. Citrus will use the method name as the test name by default. As you can see in the example above you can also customize the test name within the @CitrusTest annotation. The test method builds all test actions using the test builder pattern. The defined test actions will then be called later on during test runtime.

The design time runtime difference in test-designer is really important to be understood. You can mix the Citrus Java DSL execution with other Java code with certain limitations. We will explain this later on when introducing the test-runner .

This is the basic test Java class pattern used in Citrus. You as a tester with development background can easily extend this pattern for customized logic. Again if you are coming without coding experience do not worry this Java code is optional. You can do exactly the same with the XML syntax only as shown before. The test designer Java DSL is much more powerful though as you can use the full Java programming language with class inheritance and method delegation.

We have mentioned that the test-designer will build the complete test case in design time with all actions first before execution of the whole test case takes place at runtime of the test. This approach has the advantage that Citrus knows all test actions in a test before execution. On the other hand you are limited in mixing Java DSL method calls and normal Java code. The following example should clarify things a little bit.

Java DSL designer
import org.testng.annotations.Test;
import com.consol.citrus.annotations.CitrusTest;
import com.consol.citrus.dsl.testng.TestNGCitrusTestDesigner;

@Test
public class LoggingTestDesigner extends TestNGCitrusTestDesigner {
    private LoggingService loggingService = new LoggingService();

    @CitrusTest(name = "LoggingTest")
    public void loggingTest() {
        echo("Before loggingService call");

        loggingService.log("Now called custom logging service");

        echo("After loggingService call");
    }
}

In this example test case above we use an instance of a custom LoggingService and call some operation log() in the middle of our Java DSL test. Now developers might expect the logging service call to be done in the middle of the Java Citrus test case but if we have a look at the logging output of the test we get a total different result:

Expected output
INFO            Citrus| STARTING TEST LoggingTest
INFO        EchoAction| Before loggingService call
INFO    LoggingService| Now called custom logging service
INFO        EchoAction| After loggingService call
INFO            Citrus| TEST SUCCESS LoggingTest
Actual output
INFO    LoggingService| Now called custom logging service
INFO            Citrus| STARTING TEST LoggingTest
INFO        EchoAction| Before loggingService call
INFO        EchoAction| After loggingService call
INFO            Citrus| TEST SUCCESS LoggingTest

So if we analyse the actual logging output we see that the logging service was called even before the Citrus test case did start its action. This is the result of test-designer building up the whole test case first. The designer collects all test actions first in internal memory cache and the executes the whole test case. So the custom service call on the LoggingService is not part of the Citrus Java DSL test and therefore is executed immediately at design time.

We can fix this with the following test-designer code:

Java DSL designer
import org.testng.annotations.Test;
import com.consol.citrus.annotations.CitrusTest;
import com.consol.citrus.dsl.testng.TestNGCitrusTestDesigner;

@Test
public class LoggingTestDesigner extends TestNGCitrusTestDesigner {
    private LoggingService loggingService = new LoggingService();

    @CitrusTest(name = "LoggingTest")
    public void loggingTest() {
        echo("Before loggingService call");

        action(new AbstractTestAction() {
            doExecute(TestContext context) {
                loggingService.log("Now called custom logging service");
            }
        });

        echo("After loggingService call");
    }
}

Now we placed the loggingService call inside a custom TestAction implementation and therefore this piece of code is part of the Citrus Java DSL and following from that part of the Citrus test execution. Now with that fix we get the expected logging output:

INFO            Citrus| STARTING TEST LoggingTest
INFO        EchoAction| Before loggingService call
INFO    LoggingService| Now called custom logging service
INFO        EchoAction| After loggingService call
INFO            Citrus| TEST SUCCESS LoggingTest

Now this is not easy to understand and people did struggle with this separation of designtime and runtime of a Citrus Java DSL test. This is why we have implemented a new Java DSL base class called test-runner that we deal with in the next section. Before we continue we have to mention that the test-designer approach does also work for JUnit. Although we have only seen TestNG sample code in this section everything is working exactly the same way with JUnit framework. Just use the base class com.consol.citrus.dsl.junit.JUnit4CitrusTestDesigner instead.

Neither TestNGCitrusTestDesigner nor JUnit4CitrusTestDesigner implementation is thread safe for parallel test execution. This is simply because the base class is holding state to the current test designer instance in order to delegate method calls to this instance. Therefore parallel test method execution is not available. Fortunately we have added a threadsafe base class implementation that uses resource injection. Read more about this in test-resource-injection.

5.4. Java DSL test runner

The new test runner concept solves the issues that may come along when working with the test designer. We have already seen a simple example where the test designer requires strict separation of designtime and runtime. The test runner implementation executes each test action immediately. This changes the prerequisites in such that the test action Java DSL method calls can be mixed with usual Java code statements. The the example that we have seen before in a test runner implementation:

Java DSL runner
import org.testng.annotations.Test;
import com.consol.citrus.annotations.CitrusTest;
import com.consol.citrus.dsl.testng.TestNGCitrusTestRunner;

@Test
public class LoggingTestRunner extends TestNGCitrusTestRunner {
    private LoggingService loggingService = new LoggingService();

    @CitrusTest(name = "LoggingTest")
    public void loggingTest() {
        echo("Before loggingService call");

        loggingService.log("Now called custom logging service");

        echo("After loggingService call");
    }
}

With the new test runner implementation as base class we are able to mix Java DSL method calls and normal Java code statement in our test in an unlimited way. This example above will also create the expected logging output as all Java DSL method calls are executed immediately.

INFO            Citrus| STARTING TEST LoggingTest
INFO        EchoAction| Before loggingService call
INFO    LoggingService| Now called custom logging service
INFO        EchoAction| After loggingService call
INFO            Citrus| TEST SUCCESS LoggingTest

In contrary to the test designer the test runner implementation will not build the complete test case before execution. Each test action is executed immediately as it is called with Java DSL builder methods. This creates a more natural way of coding test cases as you are also able to use iterations, try catch blocks, finally sections and so on.

In the examples here TestNG was used as unit framework. Of course the exact same approach can also apply to JUnit framework. Just use the base class com.consol.citrus.dsl.junit.JUnit4CitrusTestRunner instead. Feel free to choose the base class for test-designer or test-runner as you like. You can also mix those two approaches in your project. Citrus is able to handle both ways of Java DSL code in a project.

The TestNGCitrusTestRunner and JUnit4CitrusTestRunner implementation is not thread safe for parallel test execution. This is simply because the base class is holding state to the current test runner instance in order to delegate method calls to this instance. Therefore parallel test method execution is not available. Fortunately we have added a threadsafe base class implementation that uses resource injection. Read more about this in test-resource-injection.

5.5. Designer/Runner injection

In the previous sections we have seen the different approaches for test designer and runner implementations. Up to now the decision which implementation to use was made by extending one of the base classes:

  • com.consol.citrus.dsl.testng.TestNGCitrusTestRunner

  • com.consol.citrus.dsl.testng.TestNGCitrusTestDesigner

  • com.consol.citrus.dsl.junit.JUnit4CitrusTestRunner

  • com.consol.citrus.dsl.junit.JUnit4CitrusTestDesigner

These four classes represent the different designer and runner implementations for TestNG or JUnit. Now Citrus also provides a resource injection mechanism for both designer and runner implementations. The classes using this feature are:

  • com.consol.citrus.dsl.testng.TestNGCitrusTest

  • com.consol.citrus.dsl.junit.JUnit4CitrusTest

So what is the deal with that? It is simple when looking at a first example using resource injection:

@Test
public class InjectionTest extends JUnit4CitrusTest {

    @CitrusTest(name = "JUnit4DesignerTest")
    public void designerTest(@CitrusResource TestDesigner designer) {
        designer.echo("Now working on designer instance");
    }

    @CitrusTest(name = "JUnit4RunnerTest")
    public void runnerTest(@CitrusResource TestRunner runner) {
        runner.echo("Now working on runner instance");
    }
}

The designer or runner instance is injected as Citrus resource to the test method as parameter. This way we can mix designer and runner in a single test. But this is not the real motivation for the resource injection. The clear advantage of this approach with injected designer and runner instances is support for multi threading. In case you want to execute the Citrus tests in parallel using multiple threads you need to use this approach. This is because the usual designer and runner base classes are not thread safe. This JUnit4CitrusTest base class is because the resources injected are not kept as state in the base class.

This is our first Citrus resource injection use case. The framework is able to inject other resources, too. Find out more about this in the next sections.

5.6. Test context injection

When running a test case in Citrus we make use of basic framework components and capabilities. One of these capabilities is to use test variables, functions and validation matchers. Up to this point we have not learned about these things. They will be described in the upcoming chapters and sections in more detail. Right now I want to talk about resource injection in Citrus.

All these feature mentioned above are bound to some important Citrus component: the Citrus test context. The test context holds all variables and is able to resolve functions and matchers. In general you as a tester will not need explicit access to this component as the framework is working with it behind the scenes. In case you need some access for advanced operations with the framework Citrus provides a resource injection. Lets have a look at this so things are getting more clear.

public class ResourceInjectionIT extends JUnit4CitrusTestDesigner {

    @Test
    @CitrusTest
    public void resourceInjectionIT(@CitrusResource TestContext context) {
        context.setVariable("myVariable", "some value");
        echo("${myVariable}");
    }
}

As you can see we have added a method parameter of type com.consol.citrus.context.TestContext to the test method. The annotation @CitrusResource tells Citrus to inject this parameter with the according instance of the object for this test. Now we have easy access to the context and all its capabilities such as variable management.

Of course the same approach works with TestNG, too. As TestNG also provides resource injection mechanisms we have to make sure that the different resource injection approaches do not interfere with each other. So we tell TestNG to not inject this parameter by declaring it as @Optional for TestNG. In addition to that we need to introduce the parameter to TestNG with the @Parameters annotation. Otherwise TestNG would complain about not knowing this parameter. The final test method with Citrus resource injection looks like follows:

public class ResourceInjectionIT extends TestNGCitrusTestDesigner {

    @Test @Parameters("context")
    @CitrusTest
    public void resourceInjectionIT(@Optional @CitrusResource TestContext context) {
        context.setVariable("myVariable", "some value");
        echo("${myVariable}");
    }
}

Some more annotations needed but the result is the same. We have access to the Citrus test context. Of course you can combine the resource injection for different Citrus components. Just add more some @CitrusResource annotated method parameters to the test method.

5.7. Java DSL test behaviors

When using the Java DSL the concept of behaviors is a good way to reuse test action blocks. By putting test actions to a test behavior we can instantiate and apply the behavior to different test cases multiple times. The mechanism is explained best when having a simple sample:

public class FooBehavior extends AbstractTestBehavior {
    public void apply() {
        variable("foo", "test");

        echo("fooBehavior");
    }
}

public class BarBehavior extends AbstractTestBehavior {
    public void apply() {
        variable("bar", "test");

        echo("barBehavior");
    }
}

The listing above shows two test behaviors that add very specific test actions and test variables to the test case. As you can see the test behavior is able to use the same Java DSL action methods as a normal test case would do. Inside the apply method block we define the behaviors test logic. Now once this is done we can use the behaviors in a test case like this:

@CitrusTest
public void behaviorTest() {
    description("This is a behavior Test");
    author("Christoph");
    status(TestCaseMetaInfo.Status.FINAL);

    variable("var", "test");

    applyBehavior(new FooBehavior());

    echo("Successfully applied bar behavior");

    applyBehavior(new BarBehavior());

    echo("Successfully applied bar behavior");
}

The behavior is applied to the test case by calling the applyBehavior method. As a result the behavior is called adding its logic at this point of the test execution. The same behavior can now be called in multiple test cases so we have a reusable set of test actions.

5.8. Description

In the test examples that we have seen so far you may have noticed that a tester can give a detailed test description. The test case description clarifies the testing purpose and perspectives. The description should give a short introduction to the intended use case scenario that will be tested. The user should get a first impression what the test case is all about as well as special information to understand the test scenario. You can use free text in your test description no limit to the number of characters. But be aware of the XML validation rules of well formed XML when using the XML test syntax (e.g. special character escaping, use of CDATA sections may be required)

5.9. Test Actions

Now we get close to the main part of writing an integration test. A Citrus test case defines a sequence of actions that will take place during the test. Actions by default are executed sequentially in the same order as they are defined in the test case definition.

XML DSL
<actions>
    <action>[...]</action>
    <action>[...]</action>
</actions>

All actions have individual names and properties that define the respective behavior. Citrus offers a wide range of test actions from scratch, but you are also able to write your own test actions in Java or Groovy and execute them during a test. actions gives you a brief description of all available actions that can be part of a test case execution.

The actions are combined in free sequence to each other so that the tester is able to declare a special action chain inside the test. These actions can be sending or receiving messages, delaying the test, validating the database and so on. Step-by-step the test proceeds through the action chain. In case one single action fails by reason the whole test case is red and declared not successful.

5.10. Finally test section

Java developers might be familiar with the concept of doing something in the finally code section. The finally section contains a list of test actions that will be executed guaranteed at the very end of the test case even if errors did occur during the execution before. This is the right place to tidy up things that were previously created by the test like cleaning up the database for instance. The finally section is described in more detail in finally-section. However here is the basic syntax inside a test.

XML DSL
<finally>
    <echo>
        <message>Do finally - regardless of what has happened before</message>
    </echo>
</finally>
Java DSL designer
@CitrusTest
public void sampleTest() {
    echo("Hello Test Framework");

    doFinally(
        echo("Do finally - regardless of any error before")
    );
}
Java DSL runner
@CitrusTest
public void sampleTest() {
    echo("Hello Test Framework");

    doFinally()
        .actions(
            echo("Do finally - regardless of any error before")
        );
}

5.11. Test meta information

The user can provide some additional information about the test case. The meta-info section at the very beginning of the test case holds information like author, status or creation date. In detail the meta information is specified like this:

XML DSL
<testcase name="metaInfoTest">
    <meta-info>
        <author>Christoph Deppisch</author>
        <creationdate>2008-01-11</creationdate>
        <status>FINAL</status>
        <last-updated-by>Christoph Deppisch</last-updated-by>
        <last-updated-on>2008-01-11T10:00:00</last-updated-on>
    </meta-info>
    <description>
        ...
    </description>
    <actions>
        ...
    </actions>
</testcase>
Java DSL
@CitrusTest
public void sampleTest() {
    description("This is a Test");
    author("Christoph");
    status(Status.FINAL);

    echo("Hello Citrus!");
}

The status allows following values: DRAFT, READY_FOR_REVIEW, DISABLED, FINAL. The meta-data information to a test is quite important to give the reader a first information about the test. It is also possible to generate test documentation using this meta-data information. The built-in Citrus documentation generates HTML or Excel documents that list all tests with their metadata information and description.

Tests with the status DISABLED will not be executed during a test suite run. So someone can just start adding planned test cases that are not finished yet in status DRAFT. In case a test is not runnable yet because it is not finished, someone may disable a test temporarily to avoid causing failures during a test run. Using these different statuses one can easily set up test plans and review the progress of test coverage by comparing the number of DRAFT tests to those in the FINAL state.

Now you know the possibilities how to write Citrus test cases in XML or Java. Please choose whatever code language type you want (Java, XML, Spring bean syntax) in order to write Citrus test cases. Developers may choose Java, testers without coding experience may run best with the XML syntax. We are constantly working on even more test writing language support such as Groovy, Scala, Xtext, and so on. In general you can mix the different language types just as you like within your Citrus project which gives you the best of flexibility.

6. Test variables

The usage of test variables is a core concept when writing good maintainable tests. The key identifiers of a test case should be exposed as test variables at the very beginning of a test. This way hard coded identifiers and multiple redundant values inside the test can be avoided from scratch. As a tester you define all test variables at the very beginning of your test.

XML DSL
<variables>
    <variable name="text" value="Hello Test Framework"/>
    <variable name="customerId" value="123456789"/>
</variables>
Java DSL
variable("text", "Hello Test Framework");
variable("customerId", "123456789");

The concept of test variables is essential when writing complex tests with lots of identifiers and semantic data. Test variables are valid for the whole test case. You can reference them several times using a common variable expression "${variable-name}" . It is good practice to provide all important entities as test variables. This makes the test easier to maintain and more flexible. All essential entities and identifiers are present right at the beginning of the test, which may also give the opportunity to easily create test variants by simply changing the variable values for other test scenarios.

The name of the variable is arbitrary. Feel free to specify any name you can think of. Of course you need to be careful with special characters and reserved XML entities like '&', '<', '>'. If you are familiar with Java or any other programming language simply think of the naming rules there and you will be fine with working on Citrus variables, too. The value of a variable can be any character sequence. But again be aware of special XML characters like "<" that need to be escaped ("<") when used in variable values.

The advantage of variables is obvious. Once declared the variables can be referenced many times in the test. This makes it very easy to vary different test cases by adjusting the variables for different means (e.g. use different error codes in test cases).

6.1. Global variables

The last section told us to use variables as they are very useful and extend the maintainability of test cases. Now that every test case defines local variables you can also define global variables. The global variables are valid in all tests by default. This is a good opportunity to declare constant values for all tests. As these variables are global we need to add those to the basic Spring application context file. The following example demonstrates how to add global variables in Citrus:

<citrus:global-variables>
    <citrus:variable name="projectName" value="Citrus Integration Testing"/>
    <citrus:variable name="userName" value="TestUser"/>
</citrus:global-variables>

We add the Spring bean component to the application context file. The component receives a list of name-value variable elements. You can reference the global variables in your test cases as usual.

Another possibility to set global variables is to load those from external property files. This may give you more powerful global variables with user specific properties for instance. See how to load property files as global variables in this example:

<citrus:global-variables>
    <citrus:file path="classpath:global-variable.properties"/>
</citrus:global-variables>

We have just added a file path reference to the global variables component. Citrus loads the property file content as global test variables. You can mix property file and name-value pair variable definitions in the global variables component.

The global variables can have variable expressions and Citrus functions. It is possible to use previously defined global variables as values of new variables, like in this example:
user=Citrus
greeting=Hello ${user}!
date=citrus:currentDate('yyyy-MM-dd')

6.2. Create variables with CDATA

When using th XML test case DSL we can not have XML variable values out of the box. This would interfere with the XML DSL elements defined in the Citrus testcase XSD schema. You can use CDATA sections within the variable value element in order to do this though.

<variables>
    <variable name="persons">
        <value>
            <data>
                <![CDATA[
                  <persons>
                    <person>
                      <name>Theodor</name>
                      <age>10</age>
                    </person>
                    <person>
                      <name>Alvin</name>
                      <age>9</age>
                    </person>
                  </persons>
                ]]>
            </data>
        </value>
    </variable>
</variables>

That is how you can use XML variable values in the XML DSL. In the Java DSL we do not have these problems.

6.3. Create variables with Groovy

You can also use a script to create variable values. This is extremely handy when you have very complex variable values. Just code a small Groovy script for instance in order to define the variable value. A small sample should give you the idea how that works:

<variables>
    <variable name="avg">
        <value>
            <script type="groovy">
                <![CDATA[
                    a = 4
                    b = 6
                    return (a + b) / 2
                ]]>
            </script>
        </value>
    </variable>
    <variable name="sum">
        <value>
            <script type="groovy">
                <![CDATA[
                    5 + 5
                ]]>
            </script>
        </value>
    </variable>
</variables>

We use the script code right inside the variable value definition. The value of the variable is the result of the last operation performed within the script. For longer script code the use of `<![CDATA[ ]]>` sections is recommended.

Citrus uses the javax ScriptEngine mechanism in order to evaluate the script code. By default Groovy is supported in any Citrus project. So you can add additional ScriptEngine implementations to your project and support other script types, too.

6.4. Escaping variables expression

The test variables expression syntax "${variable-name}" is preserved to evaluate to a test variable within the current test context. However the same syntax may be part of a message content as is. So you need to somehow escape the syntax from beeing interpreted as test variable syntax. You can do this by using the variable expression escaping // character sequence wrapping the actual variable name like this

This is a escaped variable expression ${//escaped//} and should not lead to unknown variable exceptions within Citrus.

The escaped expression ${//escaped//} above will result in the string ${escaped} where escaped is not treated as a test variable name but as a normal string in the message payload. This way you are able to have the same variable syntax in a message content without interfering with the Citrus variable expression syntax. As a result Citrus will not complain about not finding the test variable escaped in the current context. The variable syntax escaping characters // are automatically removed when the expression is processed by Citrus. So we will get the following result after processing.

This is a escaped variable expression ${escaped} and should not lead to unknown variable exceptions within Citrus.

7. Running tests

Citrus test cases are nothing but Java classes that get executed within a Java runtime environment. Each Citrus test therefore relates to a Java class representing a JUnit or TestNG unit test. As optional add on a Citrus test can have a XML test declaration file. This is for those of you that do not want to code in Java. In this case the XML part holds all actions to tell Citrus what should happen in the test case. The Java part will then just be responsible for test execution and is not likely to be changed at all. In the following sections we concentrate on the Java part and the test execution mechanism.

If you create new test cases in Citrus - for instance via Maven plugin or ANT build script - Citrus generates both parts in your test directory. For example: if you create a new test named MyFirstCitrusTest you will find these two files as a result:

src/it/tests/com/consol/citrus/MyFirstCitrusTest.xml
src/it/java/com/consol/citrus/MyFirstCitrusTest.java
If you prefer to just write Java code you can throw away the XML part immediately and continue working with the Java part only. In case you are familiar with writing Java code you may just skip the test template generation via Maven or ANT and preferably just create new Citrus Java test classes on your own.

With the creation of this test we have already made a very important decision. During creation, Citrus asks you which execution framework should be used for this test. There are basically three options available: testng and junit .

So why is Citrus related to Unit tests although it is intended to be a framework for integration testing? The answer to this question is quite simple: This is because Citrus wants to benefit from both JUnit and TestNG for Java test execution. Both the JUnit and TestNG Java APIs offer various ways of execution and both frameworks are widely supported by other tools (e.g. continuous build, build lifecycle, development IDE).

Users might already know one of these frameworks and the chances are good that they are familiar with at least one of them. Everything you can do with JUnit and TestNG test cases you can do with Citrus tests also. Include them into your Maven build lifecycle. Execute tests from your IDE (Eclipse, IDEA or NetBeans). Include them into a continuous build tool (e.g. Jenkins). Generate test execution reports and test coverage reports with Sonar, Cobertura and so on. The possibilities with JUnit and TestNG are amazing.

So let us have a closer look at the Citrus TestNG and JUnit integration.

7.1. Run with TestNG

TestNG stands for next generation testing and has had a great influence in adding Java annotations to the unit test community. Citrus is able to generate TestNG Java classes that are executable as test cases. See the following standard template that Citrus will generate when having new test cases:

TestNG Citrus XML test
package com.consol.citrus.samples;

import org.testng.annotations.Test;
import com.consol.citrus.annotations.CitrusXmlTest;
import com.consol.citrus.testng.AbstractTestNGCitrusTest;

@Test
public class SampleIT extends AbstractTestNGCitrusTest {
    @CitrusXmlTest(name = "SampleIT")
    public void sampleTest() {}
}

If you are familiar with TestNG you will see that the generated Java class is nothing but a normal TestNG test class. We just extend a basic Citrus TestNG class which enables the Citrus test execution features for us. Besides that we have a usual TestNG @Test annotation placed on our class so all methods inside the class will be executed as separate test case.

The good news is that we can still use the fantastic TestNG features in our test class. You can think of parallel test execution, test groups, setup and tear down operations and so on. Just to give an example we can simply add a test group to our test like this:

@Test(groups = {"long-running"})

For more information on TestNG please visit the official homepage, where you find a complete reference documentation.

You might have noticed that the example above loads test cases from XML. This is why we are using the @CitrusXmlTest annotation. Again this approach is for people that want to write no Java code. The test logic is then provided in the XML test definition. We discuss XML tests in Citrus in more detail in run-xml-tests. Next lets have a look at a TestNG Java DSL test.

When writing tests in pure Java we have pretty much the exact same logic that applies to executing Citrus test cases. The Citrus test extends from a TestNG base class and uses the normal @Test annotations on method or class level. Here is a short sample TestNG Java class for this:

TestNG Citrus Java DSL designer test
import org.testng.annotations.Test;
import com.consol.citrus.annotations.CitrusTest;
import com.consol.citrus.dsl.testng.TestNGCitrusTestDesigner;

@Test
public class MyFirstTestDesigner extends TestNGCitrusTestDesigner {
    @CitrusTest(name = "MyFirstIT")
    public void myFirstTest() {
        description("First example showing the basic test case definition elements!");

        variable("text", "Hello Test Framework");

        echo("${test}");
    }
}

You see the class is quite similar to the XML test variation. Now we extend a Citrus test designer class which enables the Java DSL features in addition to the TestNG test execution for us. The basic @Test annotation for TestNG has not changed. We still have a usual TestNG class with the possibility of several methods each representing a separate unit test.

Now what has changed is the @CitrusTest annotation. Now the Citrus test logic is placed directly as the method body with using the Java domain specific language features. The XML Citrus test part is not necessary anymore. If you are wondering about the designer super class and the Java DSL methods for adding the test logic to your test please be patient we will learn more about the Java DSL features in this reference guide later on.

Up to now we just concentrate on the TestNG integration that is quite easy isn’t it.

7.2. Using TestNG DataProviders

TestNG as a framework comes with lots of great features such as data providers. Data providers execute a test case several times. Each test execution gets a specific parameter value. With Citrus you can use those data provider parameters inside the test as variables. See the next listing on how to use TestNG data providers in Citrus:

TestNG Citrus data provider test
public class DataProviderIT extends AbstractTestNGCitrusTest {

  @CitrusXmlTest
  @CitrusParameters("message")
  @Test(dataProvider = "messageDataProvider")
  public void DataProviderIT(ITestContext testContext) {
  }

  @DataProvider
  public Object[][] messageDataProvider() {
  return new Object[][] {
      { "Hello World!" },
      { "Hallo Welt!" },
      { "Hi Citrus!" },
    };
  }
}

Above test case method is annotated with TestNG data provider called messageDataProvider . In the same class you can write the data provider that returns a list of parameter values. TestNG will execute the test case several times according to the provided parameter list. Each execution is shipped with the respective parameter value. According to the @CitrusParameter annotation the test will have a test variable called message that is accessible as usual.

7.3. Run with JUnit5

JUnit5 is the new major version of the famous unit testing framework. The JUnit platform provides extension points for other frameworks to integrate with the unit testing execution. Citrus uses these extensions in order to enable Citrus related dependency injection and parameter resolving.

You can use the Citrus JUnit5 extension on your unit test as follows:

JUnit5 Citrus XML test
package com.consol.citrus.samples;

import com.consol.citrus.annotations.CitrusXmlTest;
import com.consol.citrus.junit.jupiter.CitrusBaseExtension;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

/**
 * @author Christoph Deppisch
 */
@ExtendWith(CitrusBaseExtension.class)
public class SampleXmlIT {
    @Test
    @CitrusXmlTest(name = "SampleXmlIT")
    public void test() {}
}

The class above is using the JUnit5 @Test annotation as a normal unit test would do. In addition to that we extend with the CitrusBaseExtension. This enables us to use the @CitrusXmlTest annotation on the test method which automatically loads the XML test case file for execution.

In case you want to use the Citrus Java DSL for writing the test logic you can use the following setup:

JUnit5 Citrus Java DSL designer test
package com.consol.citrus.samples;

import com.consol.citrus.annotations.CitrusResource;
import com.consol.citrus.annotations.CitrusTest;
import com.consol.citrus.dsl.design.TestDesigner;
import com.consol.citrus.dsl.junit.jupiter.CitrusExtension;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

/**
 * @author Christoph Deppisch
 */
@ExtendWith(CitrusExtension.class)
public class SampleIT {
    @Test
    @CitrusTest
    public void test(@CitrusResource TestDesigner designer) {
        designer.variable("time", "citrus:currentDate()");
        designer.echo("Hello Citrus!");
        designer.echo("CurrentTime is: ${time}");
    }
}

The Java DSL test case is using the CitrusExtension to extend the JUnit5 test with Citrus functionality. After doing that we can use @CitrusResource annotated method parameters that inject the test designer. The designer is the entrance to the Java fluent API provided by Citrus. Of course you can also inject the test runner fluent API.

JUnit5 Citrus Java DSL runner test
package com.consol.citrus.samples;

import com.consol.citrus.annotations.CitrusResource;
import com.consol.citrus.annotations.CitrusTest;
import com.consol.citrus.dsl.runner.TestRunner;
import com.consol.citrus.dsl.junit.jupiter.CitrusExtension;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

/**
 * @author Christoph Deppisch
 */
@ExtendWith(CitrusExtension.class)
public class SampleIT {
    @Test
    @CitrusTest
    public void test(@CitrusResource TestRunner runner) {
        runner.variable("time", "citrus:currentDate()");
        runner.echo("Hello Citrus!");
        runner.echo("CurrentTime is: ${time}");
    }
}

You can also use @TestContext parameter injection in order to get access to the current test context used by Citrus. Also you can inject Citrus endpoints via @CitrusEndpoint annotated field injection in your test class. This enabled you to inject endpoint components that are defined in the Citrus Spring application context configuration.

JUnit5 Citrus Java DSL runner test
package com.consol.citrus.samples;

import com.consol.citrus.annotations.*;
import com.consol.citrus.dsl.runner.TestRunner;
import com.consol.citrus.dsl.junit.jupiter.CitrusExtension;
import com.consol.citrus.http.client.HttpClient;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.http.HttpStatus;

/**
 * @author Christoph Deppisch
 */
@ExtendWith(CitrusExtension.class)
public class SampleIT {

    @CitrusEndpoint
    private HttpClient httpClient;

    @Test
    @CitrusTest
    public void test(@CitrusResource TestRunner runner) {
        runner.http(action -> action.client(httpClient)
                                .send()
                                .get("/hello"));

        runner.http(action -> action.client(httpClient)
                                .receive()
                                .response(HttpStatus.OK));
    }
}

7.4. Run with JUnit4

JUnit is a very popular unit test framework for Java applications widely accepted and widely supported by many tools. In general Citrus supports both JUnit and TestNG as test execution frameworks. Although the TestNG customization features are slightly more powerful than those offered by JUnit you as a Citrus user should be able to use the framework of your choice. The complete support for executing test cases with package scans and multiple annotated methods is given for both frameworks. If you choose junit as execution framework Citrus generates a Java file that looks like this:

JUnit4 Citrus XML test
package com.consol.citrus.samples;

import org.junit.Test;
import com.consol.citrus.annotations.CitrusXmlTest;
import com.consol.citrus.junit.AbstractJUnit4CitrusTest;

public class SampleIT extends AbstractJUnit4CitrusTest {
    @Test
    @CitrusXmlTest(name = "SampleIT")
    public void sampleTest() {}
}

JUnit and TestNG as frameworks reveal slight differences, but the idea is the same. We extend a base JUnit Citrus test class and have one to many test methods that load the XML Citrus test cases for execution. As you can see the test class can hold several annotated test methods that get executed as JUnit tests. The fine thing here is that we are still able to use all JUnit features such as before/after test actions or enable/disable tests.

The Java JUnit classes are simply responsible for loading and executing the Citrus test cases. Citrus takes care on loading the XML test as a file system resource and to set up the Spring application context. The test is executed and success/failure state is reported exactly like a usual JUnit unit test would do. This also means that you can execute this Citrus JUnit class like every other JUnit test, especially out of any Java IDE, with Maven, with ANT and so on. This means that you can easily include the Citrus test execution into you software building lifecycle and continuous build.

So now we know both TestNG and JUnit support in Citrus. Which framework should someone choose? To be honest, there is no easy answer to this question. The basic features are equivalent, but TestNG offers better possibilities for designing more complex test setup with test groups and tasks before and after a group of tests. This is why TestNG is the default option in Citrus. But in the end you have to decide on your own which framework fits best for your project.

The first example seen here is using @CitrusXmlTest annotation in order to load a XML file as test. The Java part is then just an empty envelope for executing the test with JUnit. This approach is for those of you that are not familiar with Java at all. You can find more information on loading XML files as Citrus tests in run-xml-tests. Secondly of course we also have the possibility to use the Citrus Java DSL with JUnit. See the following example on how this looks like:

JUnit4 Citrus Java DSL test
package com.consol.citrus.samples;

import com.consol.citrus.annotations.CitrusTest;
import com.consol.citrus.dsl.JUnit4CitrusTestDesigner;
import org.junit.Test;

public class SampleIT extends JUnit4CitrusTestDesigner {

    @Test
    @CitrusTest
    public void EchoSampleIT() {
        variable("time", "citrus:currentDate()");
        echo("Hello Citrus!");
        echo("CurrentTime is: ${time}");
    }

    @Test
    @CitrusTest(name = "EchoIT")
    public void echoTest() {
        echo("Hello Citrus!");
    }
}

The Java DSL test case looks quite familiar as we also use the JUnit4 @Test annotation in order to mark our test for unit test execution. In addition to that we add a @CitrusTest annotation and extend from a basic JUnit4 Citrus test designer which enables the Java domain specific language features. The Citrus test logic goes directly to the method block. There is no need for a XML test file anymore.

As you can see the @CitrusTest annotation supports multiple test methods in one single class. Each test is prepared and executed separately just as you know it from JUnit. You can define an explicit Citrus test name that is used in Citrus test reports. If no explicit test name is given the test method name will be used as a test name.

If you need to know more details about the test designer and on how to use the Citrus Java DSL just continue with this reference guide. We will describe the capabilities in detail later on.

7.5. Running XML tests

Now we also use the @CitrusXmlTest annotation in the Java class. This annotation makes Citrus search for a XML file that represents the Citrus test within your classpath. Later on we will also discuss another Citrus annotation (@CitrusTest) which stands for defining the Citrus test just with Java domain specific language features. For now we continue to deal with the XML Citrus test execution.

The default naming convention requires a XML file with the tests name in the same package that the Java class is placed in. In the basic example above this means that Citrus searches for a XML test file in com/consol/citrus/samples/SampleIT.xml . You tell Citrus to search for another XML file by using the @CitrusXmlTest annotation properties. Following annotation properties are valid:

name

List of test case names to execute. Names also define XML file names to look for (.xml file extension is not needed here).

packageName

Custom package location for the XML files to load

packageScan

List of packages that are automatically scanned for XML test files to execute. For each XML file found separate test is executed. Note that this performs a Java Classpath package scan so all XML files in package are assumed to be valid Citrus XML test cases. In order to minimize the amount of accidentally loaded XML files the scan will only load XML files with **/*Test.xml and **/*IT.xml file name pattern.

You can also mix the various CitrusXmlTest annotation patterns in a single Java class. So we are able to have several test cases in one single Java class. Each annotated method represents one or more Citrus XML test cases. Se the following example to see what this is about.

TestNG Citrus XML test
@Test
public class SampleIT extends AbstractTestNGCitrusTest {
    @CitrusXmlTest(name = "SampleIT")
    public void sampleTest() {}

    @CitrusXmlTest(name = { "SampleIT", "AnotherIT" })
    public void multipleTests() {}

    @CitrusXmlTest(name = "OtherIT", packageName = "com.other.testpackage")
    public void otherPackageTest() {}

    @CitrusXmlTest(packageScan =  { "com.some.testpackage", "com.other.testpackage" })
    public void packageScanTest() {}
}

You are free to combine these test annotations as you like in your class. As the whole Java class is annotated with the TestNG @Test annotation each method gets executed automatically. Citrus will also take care on executing each XML test case as a separate unit test. So the test reports will have the exact number of executed tests and the JUnit/TestNG test reports do have the exact test outline for further usage (e.g. in continuous build reports).

When test execution takes place each test method annotation is evaluated in sequence. XML test cases that match several times, for instance by explicit name reference and a package scan will be executed several times respectively.

The best thing about using the @CitrusXmlTest annotation is that you can continue to use the fabulous TestNG capabilities (e.g. test groups, invocation count, thread pools, data providers, and so on).

So now we have seen how to execute a Citrus XML test with TestNG.

8. Configuration

You have several options in customizing the Citrus project configuration. Citrus uses default settings that can be overwritten to some extend. As a framework Citrus internally works with the Spring IoC container. So Citrus will start a Spring application context and register several components as Spring beans. You can customize the behavior of these beans and you can add custom settings by setting system properties.

8.1. Application environment settings

Citrus as an application reads general settings from system properties and environment variables. The mechanism used is based on the property placeholder resource management. Application settings are read on startup by evaluating system properties first. After that environment variables get consulted for default values. If non of these is set the default value in Citrus sources is used.

This settings mechanism is well suited for both usual Java runtime environment and containerized runtime environments such as Docker or Kubernetes. Following from that you can overwrite general Citrus application settings by just providing a system property or environment variable on your local environment. The following settings do support this kind of environment configuration.

Table 1. System properties
System properties Description

citrus.application.properties

File location for application property file that holds other settings. These properties get loaded as system properties on startup. (default="classpath:citrus-application.properties")

citrus.spring.application.context

File location for Spring XML configurations (default="classpath*:citrus-context.xml")

citrus.spring.java.config

Class name for Spring Java config (default=null)

citrus.file.encoding

Default file encoding used in Citrus when reading and writing file content (default=Charset.defaultCharset())

citrus.default.message.type

Default message type for validating payloads (default="XML")

citrus.test.name.variable

Default test name variable that is automatically created for each test (default="citrus.test.name")

citrus.test.package.variable

Default test package variable that is automatically created for each test (default="citrus.test.package")

citrus.default.src.directory

Default test source directory (default="src/test/")

citrus.xml.file.name.pattern

File name patterns used for XML test file package scan (default="/**/*Test.xml,/**/*IT.xml")

citrus.java.file.name.pattern

File name patterns used for Java test sources package scan (default="/**/*Test.java,/**/*IT.java")

Same properties are settable via environment variables.

Table 2. Environment variables
Environment variable Description

CITRUS_APPLICATION_PROPERTIES

File location for application property file that holds other settings. These properties get loaded as system properties on startup. (default="classpath:citrus-application.properties")

CITRUS_SPRING_APPLICATION_CONTEXT

File location for Spring XML configurations (default="classpath*:citrus-context.xml")

CITRUS_SPRING_JAVA_CONFIG

Class name for Spring Java config (default=null)

CITRUS_FILE_ENCODING

Default file encoding used in Citrus when reading and writing file content (default=Charset.defaultCharset())

CITRUS_DEFAULT_MESSAGE_TYPE

Default message type for validating payloads (default="XML")

CITRUS_TEST_NAME_VARIABLE

Default test name variable that is automatically created for each test (default="citrus.test.name")

CITRUS_TEST_PACKAGE_VARIABLE

Default test package variable that is automatically created for each test (default="citrus.test.package")

CITRUS_DEFAULT_SRC_DIRECTORY

Default test source directory (default="src/test/")

CITRUS_XML_FILE_NAME_PATTERN

File name patterns used for XML test file package scan (default="/**/*Test.xml,/**/*IT.xml")

CITRUS_JAVA_FILE_NAME_PATTERN

File name patterns used for Java test sources package scan (default="/**/*Test.java,/**/*IT.java")

8.2. Application property file

As mentioned in the previous section Citrus as a framework references some basic settings from system environment properties or variables. You can overwrite these settings in a central property file which is loaded at the very beginning of the Citrus runtime. The properties in that file are automatically loaded as Java system properties. Just add a property file named citrus-application.properties to your project classpath. This property file contains customized settings such as:

citrus.spring.application.context=classpath*:citrus-custom-context.xml
citrus.spring.java.config=com.consol.citrus.config.MyCustomConfig
citrus.file.encoding=UTF-8
citrus.default.message.type=XML
citrus.xml.file.name.pattern=/**/*Test.xml,/**/*IT.xml

Citrus automatically loads these application properties at startup. All properties are also settable with Java system properties. The location of the citrus-application.properties file is customizable with the system property citrus.application.properties or environment variable CITRUS_APPLICATION_PROPERTIES.

System.setProperty("citrus.application.properties", "file:/custom/path/to/citrus-application.properties")
Note

You can use classpath: and file: path prefix in order to give locate a classpath or file-system resource.

8.3. Spring XML application context

Citrus starts a Spring application context and adds some default Spring bean components. By default Citrus will load some internal Spring Java config classes defining those bean components. At some point you might add some custom beans to that basic application context. This is why Citrus will search for custom Spring application context files in your project. These are automatically loaded.

By default Citrus looks for custom XML Spring application context files in this location: classpath:citrus-context.xml* . So you can add a file named citrus-context.xml to your project classpath and Citrus will load all Spring beans automatically.

The location of this file can be customized by setting a System property citrus.spring.application.context . So you can customize the XML Spring application context file location. The System property is settable with Maven surefire and failsafe plugin for instance or via Java before the Citrus framework gets loaded.

See the following sample XML configuration which is a normal Spring bean XML configuration:

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

  <citrus:schema-repository id="schemaRepository" />

</beans>

Now you can add some Spring beans and you can use the Citrus XML components such as schema-repository for adding custom beans and components to your Citrus project. Citrus provides several namespaces for custom Spring XML components. These are described in more detail in the respective chapters and sections in this reference guide.

You can also use import statements in this Spring application context in order to load other configuration files. So you are free to modularize your configuration in several files that get loaded by Citrus.

8.4. Spring Java config

Using XML Spring application context configuration is the default behavior of Citrus. However some people might prefer pure Java code configuration. You can do that by adding a System property citrus.spring.java.config with a custom Spring Java config class as value.

System.setProperty("citrus.spring.java.config", MyCustomConfig.class.getName())

Citrus will load the Spring bean configurations in MyCustomConfig.class as Java config then. See the following example for custom Spring Java configuration:

import com.consol.citrus.TestCase;
import com.consol.citrus.report.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyCustomConfig {

    @Bean(name = "customTestListener")
    public TestListener customTestListener() {
        return new PlusMinusTestReporter();
    }

    private static class PlusMinusTestReporter extends AbstractTestListener implements TestReporter {

        /** Logger */
        private Logger log = LoggerFactory.getLogger(CustomBeanConfig.class);

        private StringBuilder testReport = new StringBuilder();

        @Override
        public void onTestSuccess(TestCase test) {
            testReport.append("+");
        }

        @Override
        public void onTestFailure(TestCase test, Throwable cause) {
            testReport.append("-");
        }

        @Override
        public void generateTestResults() {
            log.info(testReport.toString());
        }

        @Override
        public void clearTestResults() {
            testReport = new StringBuilder();
        }
    }
}

You can also mix XML and Java configuration so Citrus will load both configuration to the Spring bean application context on startup.

9. Endpoints

In one of the previous chapters we have discussed the basic test case structure as we introduced variables and test actions . The <actions> section contains a list of test actions that take place during the test case. Each test action is executed in sequential order by default. Citrus offers several built-in test actions that the user can choose from to construct a complex testing workflow without having to code everything from scratch. In particular Citrus aims to provide all the test actions that you need as predefined components ready for you to use. The goal is to minimize the coding effort for you so you can concentrate on the test logic itself.

Exactly the same approach is used in Citrus to provide ready-to-use endpoint component for connecting to different message transports. There are several ways in an enterprise application to exchange messages with some other application. We have synchronous interfaces like Http and SOAP WebServices. We have asynchronous messaging with JMS or file transfer FTP interfaces.

Citrus provides endpoint components as client and server to connect with these typical message transports. So you as a tester must not care about how to send a message to a JMS queue. The Citrus endpoints are configured in the Spring application context and receive endpoint specific properties like endpoint uri or ports or message timeouts as configuration.

The next figure shows a typical message sending endpoint component in Citrus:

figure_002.jpg

The endpoint producer publishes messages to a destination. This destination can be a JMS queue/topic, a SOAP WebService endpoint, a Http URL, a FTP folder destination and so on. The producer just takes a previously defined message definition (header and payload) and sends it to the message destination.

Similar to that Citrus defines the several endpoint consumer components to consume messages from destinations. This can be a simple subscription on message channels and JMS queues/topics. In case of SOAP WebServices and Http GET/POST things are more complicated as we have to provide a server component that clients can connect to. We will handle server related communication in more detail later on. For now the endpoint consumer component in its most simple way is defined like this:

figure_003.jpg

This is all you need to know about Citrus endpoints. We have mentioned that the endpoints are defined in the Spring application context. Let’s have a simple example that shows the basic idea:

<citrus-jms:endpoint id="helloServiceEndpoint"
        destination-name="Citrus.HelloService.Request.Queue"
        connection-factory="myConnectionFactory"/>

This is a simple JMS endpoint component in Citrus. The endpoint XML bean definition follows a custom XML namespace and defines endpoint specific properties like the JMS destination name and the JMS connection factory. The endpoint id is a significant property as the test cases will reference this endpoint when sending and receiving messages by its identifier.

In the next sections you will learn how a test case uses those endpoint components for producing and consuming messages.

9.1. Send messages with endpoints

The <send> action in a test case publishes messages to a destination. The actual message transport connection is defined with the endpoint component. The test case simply defines the message contents and references a predefined message endpoint component by its identifier. Endpoint specific configurations are centralized in the Spring bean application context while multiple test cases can reference the endpoint to actually publish the constructed message to a destination. There are several message endpoint implementations in Citrus available representing different transport protocols like JMS, SOAP, HTTP, TCP/IP and many more.

Again the type of transport to use is not specified inside the test case but in the message endpoint definition. The separation of concerns (test case/message sender transport) gives us a good flexibility of our test cases. The test case does not know anything about connection factories, queue names or endpoint uri, connection timeouts and so on. The transport internals underneath a sending test action can change easily without affecting the test case definition. We will see later in this document how to create different message endpoints for various transports in Citrus. For now we concentrate on constructing the message content to be sent.

We assume that the message’s payload will be plain XML format. Citrus uses XML as the default data format for message payload data. But Citrus is not limited to XML message format though; you can always define other message data formats such as JSON, plain text, CSV. As XML is still a very popular message format in enterprise applications and message-based solution architectures we have this as a default format. Anyway Citrus works best on XML payloads and you will see a lot of example code in this document using XML. Finally let us have a look at a first example how a sending action is defined in the test.

XML DSL
<testcase name="SendMessageTest">
    <description>Basic send message example</description>

    <actions>
        <send endpoint="helloServiceEndpoint">
            <message>
                <payload>
                    <TestMessage>
                        <Text>Hello!</Text>
                    </TestMessage>
                </payload>
            </message>
            <header>
                <element name="Operation" value="sayHello"/>
            </header>
        </send>
    </actions>
</testcase>

Now lets have a closer look at the sending action. The 'endpoint' attribute might catch your attention first. This attribute references the message endpoint in Citrus configuration by its identifier. As previously mentioned the message endpoint definition lives in a separate configuration file and contains the actual message transport settings. In this example the "helloServiceEndpoint" is referenced which is a JMS endpoint for sending out messages to a JMS queue for instance.

The test case is not aware of any transport details, because it does not have to. The advantages are obvious: On the one hand multiple test cases can reference the message endpoint definition for better reuse. Secondly test cases are independent of message transport details. So connection factories, user credentials, endpoint uri values and so on are not present in the test case.

In other words the "endpoint" attribute of the <send> element specifies which message endpoint definition to use and therefore where the message should go to. Once again all available message endpoints are configured in a separate Citrus configuration file. Be sure to always pick the right message endpoint type in order to publish your message to the right destination.

If you do not like the XML language you can also use pure Java code to define the same test. In Java you would also make use of the message endpoint definition and reference this instance. The same test as shown above in Java DSL looks like this:

Java DSL designer
import org.testng.ITestContext;
import org.testng.annotations.Test;
import com.consol.citrus.annotations.CitrusTest;
import com.consol.citrus.dsl.testng.TestNGCitrusTestDesigner;

@Test
public class SendMessageTestDesigner extends TestNGCitrusTestDesigner {

    @CitrusTest(name = "SendMessageTest")
    public void sendMessageTest() {
        description("Basic send message example");

        send("helloServiceEndpoint")
            .payload("<TestMessage>" +
                        "<Text>Hello!</Text>" +
                    "</TestMessage>")
            .header("Operation", "sayHello");
    }
}

Instead of using the XML tags for send we use methods from TestNGCitrusTestDesigner class. The same message endpoint is referenced within the send message action. The payload is constructed as plain Java character sequence which is a bit verbose. We will see later on how we can improve this. For now it is important to understand the combination of send test action and a message endpoint.

It is good practice to follow naming conventions when defining names for message endpoints. The intended purpose of the message endpoint as well as the sending/receiving actor should be clear when choosing the name. For instance messageEndpoint1, messageEndpoint2 will not give you much hints to the purpose of the message endpoint.

This is basically how to send messages in Citrus. The test case is responsible for constructing the message content while the predefined message endpoint holds transport specific settings. Test cases reference endpoint components to publish messages to the outside world. This is just the start of action. Citrus supports a whole package of other ways how to define and manipulate the message contents. Read more about message sending actions in actions-send.

9.2. Receive messages with endpoints

Now we have a look at the message receiving part inside the test. A simple example shows how it works.

XML DSL
<receive endpoint="helloServiceEndpoint">
    <message>
        <payload>
            <TestMessage>
                <Text>Hello!</Text>
            </TestMessage>
        </payload>
    </message>
    <header>
        <element name="Operation" value="sayHello"/>
    </header>
</receive>

If we recap the send action of the previous chapter we can identify some common mechanisms that apply for both sending and receiving actions. The test action also uses the endpoint attribute for referencing a predefined message endpoint. This time we want to receive a message from the endpoint. Again the test is not aware of the transport details such as JMS connections, endpoint uri, and so on. The message endpoint component encapsulates this information.

Before we go into detail on validating the received message we have a quick look at the Java DSL variation for the receive action. The same receive action as above looks like this in Java DSL.

Java DSL designer
@CitrusTest
public void messagingTest() {
    receive("helloServiceEndpoint")
        .payload("<TestMessage>" +
                    "<Text>Hello!</Text>" +
                "</TestMessage>")
        .header("Operation", "sayHello");
}

The receive action waits for a message to arrive. The whole test execution is stopped while waiting for the message. This is important to ensure the step by step test workflow processing. Of course you can specify message timeouts so the receiver will only wait a given amount of time before raising a timeout error. Following from that timeout exception the test case fails as the message did not arrive in time. Citrus defines default timeout settings for all message receiving tasks.

At this point you know the two most important test actions in Citrus. Sending and receiving actions will become the main components of your integration tests when dealing with loosely coupled message based components in a enterprise application environment. It is very easy to create complex message flows, meaning a sequence of sending and receiving actions in your test case. You can replicate use cases and test your message exchange with extended message validation capabilities. See actions-receive for a more detailed description on how to validate incoming messages and how to expect message contents in a test case.

9.3. Local message store

All messages that are sent and received during a test case are stored in a local memory storage. This is because we might want to access the message content later on in a test case. We can do so by using message store functions for loading messages that have been exchanged earlier in the test. When storing a message in the local storage Citrus uses a message name as identifier key. This message name is later on used to access the message. You can define the message name in any send or receive action:

XML DSL
<receive endpoint="helloServiceEndpoint">
    <message name="helloMessage">
        <payload>
            <TestMessage>
                <Text>Hello!</Text>
            </TestMessage>
        </payload>
    </message>
    <header>
        <element name="Operation" value="sayHello"/>
    </header>
</receive>
Java DSL designer
@CitrusTest
public void messagingTest() {
    receive("helloServiceEndpoint")
        .name("helloMessage")
        .payload("<TestMessage>" +
                    "<Text>Hello!</Text>" +
                "</TestMessage>")
        .header("Operation", "sayHello");
}

The receive operation above set the message name to helloMessage. The message received is automatically stored in the local storage with that name. You can access the message content for instance by using a function:

<echo>
    <message>citrus:message(helloMessage.payload())</message>
</echo>

The function loads the helloMessage and prints the payload information with the echo test action. In combination with Xpath or JsonPath functions this mechanism is a good way to access the exchanged message contents later in a test case.

The storage is for both sent and received messages in a test case. The storage is per test case and contains all sent and received messages.

When no explicit message name is given the local storage will construct a default message name. The default name is built from the action (send or receive) plus the endpoint used to exchange the message. For instance:

send(helloEndpoint)
receive(helloEndpoint)

The names above would be generated by a send and receive operation on the endpoint named helloEndpoint.

The message store is not able to handle multiple message of the same name in one test case. So messages with identical names will overwrite existing messages in the local storage.

Now we have seen the basic endpoint concept in Citrus. The endpoint components represent the connections to the test boundary systems. This is how we can connect to the system under test for message exchange. And this is our main goal with this integration test framework. We want to provide easy access to common message transports on client and server side so that we can test the communication interfaces on a real message transport exchange.

10. Message validation

When Citrus receives a message from external applications it is time to verify the message content. This message validation includes syntax rules as well as semantic values that need to be compared to an expected behavior. Citrus provides powerful message validation capabilities. Each incoming message is validated with syntax and semantics. The tester is able to define expected message headers and payloads. Citrus message validator implementations will compare the messages and report differences as test failure. With the upcoming sections we have a closer look at message validation of XML messages with XPath and XML schema validation and further message formats like JSON and plaintext.

10.1. XML message validation

XML is a very common message format especially in the SOAP WebServices and JMS messaging world. Citrus provides XML message validator implementations that are able to compare XML message structures. The validator will notice differences in the XML message structure and supports XML namespaces, attributes and XML schema validation. The default XML message validator implementation is active by default and can be overwritten with a custom implementation using the bean id defaultXmlMessageValidator .

<bean id="defaultXmlMessageValidator" class="com.consol.citrus.validation.xml.DomXmlMessageValidator"/>

The default XML message validator is very powerful when it comes to compare XML structures. The validator supports namespaces with different prefixes and attributes als well as namespace qualified attributes. See the following sections for a detailed description of all capabilities.

10.1.1. XML payload validation

Once Citrus has received a message the tester can validate the message contents in various ways. First of all the tester can compare the whole message payload to a predefined control message template.

The receiving action offers following elements for control message templates:

<payload>

Defines the message payload as nested XML message template. The whole message payload is defined inside the test case.

<data>

Defines an inline XML message template as nested CDATA. Slightly different to the payload variation as we define the whole message payload inside the test case as CDATA section.

<resource>

Defines an expected XML message template via external file resources. This time the payload is loaded at runtime from the external file.

Both ways inline payload definition or external file resource give us a control message template that the test case expects to arrive. Citrus uses this control template for extended message comparison. All elements, namespaces, attributes and node values are validated in this comparison. When using XML message payloads Citrus will navigate through the whole XML structure validating each element and its content. Same with JSON payloads.

Only in case received message and control message are equal to each other as expected the message validation will pass. In case differences occur Citrus gives detailed error messages and the test case fails.

The control message template is not necessarily very static. Citrus supports various ways to add dynamic message content on the one side and on the other side Citrus can ignore some elements that are not part of message comparison (e.g. when generated content or timestamps are part of the message content). The tester can enrich the expected message template with test variables or ignore expressions so we get a more robust validation mechanism. We will talk about this in the next sections to come.

When using the Citrus Java DSL you will face a verbose message payload definition. This is because Java does not support multiline character sequence values as Strings. We have to use verbose String concatenation when constructing XML message payload contents for instance. In addition to that reserved characters like quotes must be escaped and line breaks must be explicitly added. All these impediments let me suggest to use external file resources in Java DSL when dealing with large complex message payload data. Here is an example:

Java DSL designer
@CitrusTest
public void receiveMessageTest() {
    receive("helloServiceServer")
        .payload(new ClassPathResource("com/consol/citrus/message/data/TestRequest.xml"))
        .header("Operation", "sayHello")
        .header("MessageId", "${messageId}");
}

10.1.2. XML header validation

Now that we have validated the message payload in various ways we are now interested in validating the message header. This is simple as you have to define the header name and the control value that you expect. Just add the following header validation to your receiving action.

XML DSL
<header>
    <element name="Operation" value="GetCustomer"/>
    <element name="RequestTag" value="${requestTag}"/>
</header>
Java DSL designer
@CitrusTest
public void receiveMessageTest() {
    receive("helloServiceServer")
        .header("Operation", "sayHello")
        .header("MessageId", "${messageId}");
}

Message headers are represented as name-value pairs. Each expected header element identified by its name has to be present in the received message. In addition to that the header value is compared to the given control value. If a header entry is not found by its name or the value does not fit accordingly Citrus will raise validation errors and the test case will fail.

Sometimes message headers may not apply to the name-value pair pattern. For example SOAP headers can also contain XML fragments. Citrus supports these kind of headers too. Please see the SOAP chapter for more details.

10.2. Ignore XML elements

Some elements in the message payload might not apply for validation at all. Just think of communication timestamps an dynamic values inside a message:

The timestamp value in our next example will dynamically change from test run to test run and is hardly predictable for the tester, so lets ignore it in validation.

XML DSL
<message>
    <payload>
        <TestMessage>
            <MessageId>${messageId}</MessageId>
            <Timestamp>2001-12-17T09:30:47.0Z</Timestamp>
            <VersionId>@ignore@</VersionId>
        </TestMessage>
    </payload>
    <ignore path="/TestMessage/Timestamp"/>
</message>

Although we have given a static timestamp value in the payload data the element is ignored during validation as the ignore XPath expression matches the element. In addition to that we also ignored the version id element in this example. This time with an inline @ignore@ expression. This is for those of you that do not like XPath. As a result the ignored message elements are automatically skipped when Citrus compares and validates message contents and do not break the test case.

When using the Java DSL the @ignore@ placeholder as well as XPath expressions can be used seamlessly. Here is an example of that:

Java DSL designer
@CitrusTest
public void receiveMessageTest() {
    receive("helloServiceServer")
        .payload(new ClassPathResource("com/consol/citrus/message/data/TestRequest.xml"))
        .header("Operation", "sayHello")
        .header("MessageId", "${messageId}")
        .ignore("/TestMessage/Timestamp");
}

Of course you can use the inline @ignore@ placeholder in an external file resource, too.

10.2.1. Customize XML parser and serializer

When working with XML data format parsing and serializing is a common task. XML structures are parsed to a DOM (Document Object Model) representation in order to process elements, attributes and text nodes. Also DOM node objects get serialized to a String message payload representation. The XML parser and serializer is customizable to a certain level. By default Citrus uses the DOM Level 3 Load and Save implementation with following settings:

Parser settings
cdata-sections

true

split-cdata-sections

false

validate-if-schema

true

element-content-whitespace

false

Serializer settings
format-pretty-print

true

split-cdata-sections

false

element-content-whitespace

true

The parameters are also described in W3C DOM configuration documentation. We can customize the default settings by adding a XmlConfigurer Spring bean to the Citrus application context.

<bean id="xmlConfigurer" class="com.consol.citrus.xml.XmlConfigurer">
    <property name="parseSettings">
        <map>
            <entry key="validate-if-schema" value="false" value-type="java.lang.Boolean"/>
        </map>
    </property>
    <property name="serializeSettings">
        <map>
            <entry key="comments" value="false" value-type="java.lang.Boolean"/>
            <entry key="format-pretty-print" value="false" value-type="java.lang.Boolean"/>
        </map>
    </property>
</bean>
This configuration is of global nature. All XML processing operations will be affected with this configuration.

10.2.2. Groovy XML validation

With the Groovy XmlSlurper you can easily validate XML message payloads without having to deal directly with XML. People who do not want to deal with XPath may also like this validation alternative. The tester directly navigates through the message elements and uses simple code assertions in order to control the message content. Here is an example how to validate messages with Groovy script:

XML DSL
<receive endpoint="helloServiceClient" timeout="5000">
    <message>
        <validate>
            <script type="groovy">
                assert root.children().size() == 4
                assert root.MessageId.text() == '${messageId}'
                assert root.CorrelationId.text() == '${correlationId}'
                assert root.User.text() == 'HelloService'
                assert root.Text.text() == 'Hello ' + context.getVariable("user")
            </script>
        </validate>
    </message>
    <header>
        <element name="Operation" value="sayHello"/>
        <element name="CorrelationId" value="${correlationId}"/>
    </header>
</receive>
Java DSL designer
@CitrusTest
public void receiveMessageTest() {
    receive("helloServiceClient")
        .validateScript("assert root.MessageId.text() == '${messageId}';" +
                        "assert root.CorrelationId.text() == '${correlationId}';")
        .header("Operation, "sayHello")
        .header("CorrelationId", "${correlationId}")
        .timeout(5000L);
}

The Groovy XmlSlurper validation script goes right into the message-tag instead of a XML control template or XPath validation. The Groovy script supports Java assert statements for message element validation. Citrus automatically injects the root element root to the validation script. This is the Groovy XmlSlurper object and the start of element navigation. Based on this root element you can access child elements and attributes with a dot notated syntax. Just use the element names separated by a simple dot. Very easy! If you need the list of child elements use the children() function on any element. With the text() function you get access to the element’s text-value. The size() is very useful for validating the number of child elements which completes the basic validation statements.

As you can see from the example, we may use test variables within the validation script, too. Citrus has also injected the actual test context to the validation script. The test context object holds all test variables. So you can also access variables with context.getVariable("user") for instance. On the test context you can also set new variable values with context.setVariable("user", "newUserName") .

There is even more object injection for the validation script. With the automatically added object receivedMessage You have access to the Citrus message object for this receive action. This enables you to do whatever you want with the message payload or header.

XML DSL
<receive endpoint="helloServiceClient" timeout="5000">
    <message>
        <validate>
            <script type="groovy">
                assert receivedMessage.getPayload(String.class).contains("Hello Citrus!")
                assert receivedMessage.getHeader("Operation") == 'sayHello'

                context.setVariable("request_payload", receivedMessage.getPayload(String.class))
            </script>
        </validate>
    </message>
</receive>

The listing above shows some power of the validation script. We can access the message payload, we can access the message header. With test context access we can also save the whole message payload as a new test variable for later usage in the test.

In general Groovy code inside the XML test case definition or as part of the Java DSL code is not very comfortable to maintain. You do not have code syntax assist or code completion. This is why we can also use external file resources for the validation scripts. The syntax looks like follows:

XML DSL
<receive endpoint="helloServiceClient" timeout="5000">
    <message>
        <validate>
            <script type="groovy" file="classpath:validationScript.groovy"/>
        </validate>
    </message>
    <header>
        <element name="Operation" value="sayHello"/>
        <element name="CorrelationId" value="${correlationId}"/>
    </header>
</receive>
Java DSL designer
@CitrusTest
public void receiveMessageTest() {
    receive("helloServiceClient")
        .validateScript(new FileSystemResource("validationScript.groovy"))
        .header("Operation, "sayHello")
        .header("CorrelationId", "${correlationId}")
        .timeout(5000L);
}

We referenced some external file resource validationScript.groovy . This file content is loaded at runtime and is used as script body. Now that we have a normal groovy file we can use the code completion and syntax highlighting of our favorite Groovy editor.

You can use the Groovy validation script in combination with other validation types like XML tree comparison and XPath validation.
For further information on the Groovy XmlSlurper please see the official Groovy website and documentation

10.3. JSON message validation

Message formats such as JSON have become very popular, in particular when speaking of RESTful WebServices and JavaScript using JSON as the message format to go for. Citrus is able to expect and validate JSON messages as we will see in the next sections.

By default Citrus will use XML message formats when sending and receiving messages. This also reflects to the message validation logic Citrus uses for incoming messages. So by default Citrus will try to parse the incoming message as XML DOM element tree. In case we would like to enable JSON message validation we have to tell Citrus that we expect a JSON message right now.

And this is quite easy. Citrus has a JSON message validator implementation active by default and immediately as we mark an incoming message as JSON data this message validator will jump in.

Citrus provides several default message validator implementations for JOSN message format:

JsonMessageValidators
com.consol.citrus.validation.json.JsonTextMessageValidator

Basic JSON message validator implementation compares JSON objects (expected and received). The order of JSON entries can differ as specified in JSON protocol. Tester defines an expected control JSON object with test variables and ignored entries. JSONArray as well as nested JSONObjects are supported, too.

com.consol.citrus.validation.script.GroovyJsonMessageValidator

Extended groovy message validator provides specific JSON slurper support. With JSON slurper the tester can validate the JSON message payload with closures for instance.

The JSON validator offers two different modes to operate. By default strict mode is enabled and the validator will also check the exact amount of object fields to match in received and control message. No additional fields in received JSON data structure will be accepted then. In soft mode the validator allows additional fields in received JSON data structure so the control JSON object can be a partial subset in which case only the control fields are validated. Additional fields in the received JSON data structure are ignored then.
The JSON validation mode (strict or soft) is settable via system property citrus.json.message.validation.strict=false. This will set soft mode to all JSON text messag validators.

You can also overwrite this default message validators for JSON by placing a bean into the Spring Application context. The bean uses a default name as identifier. Then your custom bean will overwrite the default validator:

<bean id="defaultJsonMessageValidator" class="com.consol.citrus.validation.json.JsonTextMessageValidator"/>
<bean id="defaultGroovyJsonMessageValidator" class="com.consol.citrus.validation.script.GroovyJsonMessageValidator"/>

This is how you can customize the message validators used for JSON message data.

We have mentioned before that Citrus is working with XML by default. This is why we have to tell Citrus that the message that we are receiving uses the JSON message format. We have to tell the test case receiving action that we expect a different format other than XML.

<receive endpoint="httpMessageEndpoint">
    <message type="json">
        <data>
          {
            "type" : "read",
            "mbean" : "java.lang:type=Memory",
            "attribute" : "HeapMemoryUsage",
            "path" : "@equalsIgnoreCase('USED')@",
            "value" : "${heapUsage}",
            "timestamp" : "@ignore@"
          }
        </data>
    </message>
</receive>

The message receiving action in our test case specifies a message format type type="json" . This tells Citrus to look for some message validator implementation capable of validating JSON messages. As we have added the proper message validator to the Spring application context Citrus will pick the right validator and JSON message validation is performed on this message. As you can see you we can use the usual test variables and the ignore element syntax here, too. Citrus is able to handle different JSON element orders when comparing received and expected JSON object. We can also use JSON arrays and nested objects. The default JSON message validator implementation in Citrus is very powerful in comparing JSON objects.

Instead of defining an expected message payload template we can also use Groovy validation scripts. Lets have a look at the Groovy JSON message validator example. As usual the default Groovy JSON message validator is active by default. But the special Groovy message validator implementation will only jump in when we used a validation script in our receive message definition. Let’s have an example for that.

<receive endpoint="httpMessageEndpoint">
    <message type="json">
        <validate>
            <script type="groovy">
                <![CDATA[
                  assert json.type == 'read'
                  assert json.mbean == 'java.lang:type=Memory'
                  assert json.attribute == 'HeapMemoryUsage'
                  assert json.value == '${heapUsage}'
                ]]>
            </script>
        </validate>
    </message>
</receive>

Again we tell Citrus that we expect a message of type="json" . Now we used a validation script that is written in Groovy. Citrus will automatically activate the special message validator that executes our Groovy script. The script validation is more powerful as we can use the full power of the Groovy language. The validation script automatically has access to the incoming JSON message object json . We can use the Groovy JSON dot notated syntax in order to navigate through the JSON structure. The Groovy JSON slurper object json is automatically passed to the validation script. This way you can access the JSON object elements in your code doing some assertions.

There is even more object injection for the validation script. With the automatically added object receivedMessage You have access to the Citrus message object for this receive action. This enables you to do whatever you want with the message payload or header.

XML DSL
<receive endpoint="httpMessageEndpoint">
    <message type="json">
        <validate>
            <script type="groovy">
                assert receivedMessage.getPayload(String.class).contains("Hello Citrus!")
                assert receivedMessage.getHeader("Operation") == 'sayHello'

                context.setVariable("request_payload", receivedMessage.getPayload(String.class))
            </script>
        </validate>
    </message>
</receive>

The listing above shows some power of the validation script. We can access the message payload, we can access the message header. With test context access we can also save the whole message payload as a new test variable for later usage in the test.

In general Groovy code inside the XML test case definition or as part of the Java DSL code is not very comfortable to maintain. You do not have code syntax assist or code completion. This is why we can also use external file resources for the validation scripts. The syntax looks like follows:

XML DSL
<receive endpoint="helloServiceClient" timeout="5000">
    <message>
        <validate>
            <script type="groovy" file="classpath:validationScript.groovy"/>
        </validate>
    </message>
</receive>
Java DSL designer
@CitrusTest
public void receiveMessageTest() {
    receive("helloServiceClient")
        .validateScript(new FileSystemResource("validationScript.groovy"));
}

We referenced some external file resource validationScript.groovy . This file content is loaded at runtime and is used as script body. Now that we have a normal groovy file we can use the code completion and syntax highlighting of our favorite Groovy editor.

Using several message validator implementations at the same time in the Spring application context is also no problem. Citrus automatically searches for all available message validators applicable for the given message format and executes these validators in sequence. So several message validators can coexist in a Citrus project.

When we have multiple message validators that apply to the message format Citrus will execute all of them in sequence. In case you need to explicitly choose a message validator implementation you can do so in the receive action:

<receive endpoint="httpMessageEndpoint">
    <message type="json" validator="groovyJsonMessageValidator">
        <validate>
            <script type="groovy">
                <![CDATA[
                  assert json.type == 'read'
                  assert json.mbean == 'java.lang:type=Memory'
                  assert json.attribute == 'HeapMemoryUsage'
                  assert json.value == '${heapUsage}'
                ]]>
            </script>
        </validate>
    </message>
</receive>

In this example we use the groovyJsonMessageValidator explicitly in the receive test action. The message validator implementation was added as Spring bean with id groovyJsonMessageValidator to the Spring application context before. Now Citrus will only execute the explicit message validator. Other implementations that might also apply are skipped.

By default Citrus will consolidate all available message validators for a message format in sequence. You can explicitly pick a special message validator in the receive message action as shown in the example above. In this case all other validators will not take part in this special message validation. But be careful: When picking a message validator explicitly you are of course limited to this message validator capabilities. Validation features of other validators are not valid in this case (e.g. message header validation, XPath validation, etc.)

So much for receiving JSON message data in Citrus. Of course sending JSON messages in Citrus is also very easy. Just use JSON message payloads in your sending message action.

<send endpoint="httpMessageEndpoint">
    <message>
        <data>
          {
            "type" : "read",
            "mbean" : "java.lang:type=Memory",
            "attribute" : "HeapMemoryUsage",
            "path" : "used"
          }
        </data>
    </message>
</send>

10.4. Schema validation

When structured data is transmitted from one system to another, it’s important that the sender and the receiver understand each other. To ensure this, a contract is required introducing the rules of communication for both parties. This is mostly done by using schemas. Citrus brings the schema based validation of messages directly to your integration test environment. This includes the management, mapping and filtering of schemas in context to your current test case by just defining some rules in XML configuration or Java DSL. Let’s start with this chapter by introducing some basic concepts of the schema validation.

10.4.1. Managing schemas

Most complex applications have a lot of schemas that are relevant for different use cases. To organize these schemas in your test cases, Citrus brings you some structuring capabilities.

10.4.1.1. Schema definitions
<citrus:schema id="bookstore" location="classpath:com/consol/citrus/xml/BookStore.wsdl"/>

This xml snippet allows you to define a schema identified by its id, located at the given classpath. Please keep in mind, that the id of the schema has to be unique within the context.

10.4.1.2. Schema references
<citrus:schema id="testSchema" location="classpath:com/consol/citrus/xml/test.xsd"/>

...
<citrus:reference schema="testSchema"/>
...

Citrus allows you to reuse your schema definition within your context by referencing them. For a valid reference, the id of the schema and the value of the schema Attribute within the reference Element have to match.

10.4.1.3. Schema location definitions
<citrus:location path="classpath:citrus/flightbooking/*.xsd"/>

Setting all schemas one by one can be verbose and uncomfortable, especially when dealing with lots of schema files. Therefore, Citrus offers you schema location patterns which will import all matching schema files within the given location.

10.4.1.4. Schema repositories
<citrus:schema id="testSchema" location="classpath:com/consol/citrus/xml/test.xsd"/>

<citrus:schema-repository id="xmlSchemaRepository">
  <citrus:schemas>
    <citrus:schema id="bookstore" location="classpath:com/consol/citrus/xml/BookStore.wsdl"/>
    <citrus:reference schema="testSchema"/>
    <citrus:location path="classpath:com/consol/citrus/validation/*"/>
  </citrus:schemas>
</citrus:schema-repository>

<citrus:schema-repository type="json" id="jsonSchemaRepository">
    <citrus:schemas>
        <citrus:schema id="product" location="classpath:com/consol/citrus/validation/ProductsSchema.json"/>
    </citrus:schemas>
</citrus:schema-repository>

Citrus introduces a central schema repository component which holds a set of schema files for a project disjoint by their type (xml, json, etc.) and identified by its unique id. The default type of a schema repository is type=xml.

As you can see the schema repository is a simple XML component defined inside the Spring application context. The repository can hold nested schema definitions, references and location definitions for all types of schema repositories.

In case you have several schema repositories in your project do always define a default repository (name="schemaRepository"). This helps Citrus to always find at least one repository to interact with.

10.4.2. Schema definition overruling

Depending on the type of message you want to validate, there are different attempts to find the correct schema for the given message. There comes a time where you as a tester have to pick the right schema definition by yourself. You can overrule all schema mapping strategies in Citrus by directly setting the desired schema in your receiving message action.

<receive endpoint="httpMessageEndpoint">
    <message schema="helloSchema">
      <validate>
        <xpath expression="//ns1:TestMessage/ns1:MessageHeader/ns1:MessageId"
                  value="${messageId}"/>
        <xpath expression="//ns1:TestMessage/ns1:MessageHeader/ns1:CorrelationId"
                  value="${correlationId}"/>
        <namespace prefix="ns1" value="http://citrus.com/namespace"/>
      </validate>
    </message>
</receive>

<citrus:schema id="helloSchema"
    location="classpath:citrus/samples/sayHello.xsd"/>

In the example above the tester explicitly sets a schema definition in the receive action (schema="helloSchema"). The attribute value refers to named schema bean somewhere in the application context. This overrules all schema mapping strategies used in the central schema repository as the given schema is directly used for validation. This feature is helpful when dealing with different schema versions at the same time where the schema repository can not help you anymore.

Another possibility would be to set a custom schema repository at this point. This means you can have more than one schema repository in your Citrus project and you pick the right one by yourself in the receive action.

<receive endpoint="httpMessageEndpoint">
    <message schema-repository="mySpecialSchemaRepository">
      <validate>
        <xpath expression="//ns1:TestMessage/ns1:MessageHeader/ns1:MessageId"
                  value="${messageId}"/>
        <xpath expression="//ns1:TestMessage/ns1:MessageHeader/ns1:CorrelationId"
                  value="${correlationId}"/>
        <namespace prefix="ns1" value="http://citrus.com/namespace"/>
      </validate>
    </message>
</receive>

The schema-repository attribute refers to a Citrus schema repository component which is defined somewhere in the Spring application context.

10.4.3. XML schema validation

There are several possibilities to describe the structure of XML documents. The two most popular ways are DTD (Document type definition) and XSD (XML Schema definition). Once a XML document has decided to be classified using a schema definition the structure of the document has to fit the predefined rules inside the schema definition. XML document instances are valid only in case they meet all these structure rules defined in the schema definition. Currently Citrus can validate XML documents using the schema languages DTD and XSD.

10.4.3.1. XSD schema repositories

Citrus tries to validate all incoming XML messages against a schema definition in order to ensure that all rules are fulfilled. As a consequence the message receiving actions in Citrus do have to know the XML schema definition file resources that belong to our test context.

<citrus:schema-repository id="schemaRepository">
    <citrus:schemas>
        <citrus:schema id="travelAgencySchema"
            location="classpath:citrus/flightbooking/TravelAgencySchema.xsd"/>
        <citrus:schema id="royalArilineSchema"
            location="classpath:citrus/flightbooking/RoyalAirlineSchema.xsd"/>
        <citrus:reference schema="smartArilineSchema"/>
    </citrus:schemas>
</citrus:schema-repository>

<citrus:schema id="smartArilineSchema"
      location="classpath:citrus/flightbooking/SmartAirlineSchema.xsd"/>

By convention there is a default schema repository component defined in the Citrus Spring application context with the id schemaRepository. Spring application context is then able to inject the schema repository into all message receiving test actions at runtime. The receiving test action consolidates the repository for a matching schema definition file in order to validate the incoming XML document structure.

The connection between incoming XML messages and xsd schema files in the repository is done by a mapping strategy which we will discuss later in this chapter. By default Citrus picks the right schema based on the target namespace that is defined inside the schema definition. The target namespace of the schema definition has to match the namespace of the root element in the received XML message. With this mapping strategy you will not have to wire XML messages and schema files manually all is done automatically by the Citrus schema repository at runtime. All you need to do is to register all available schema definition files regardless of which target namespace or nature inside the Citrus schema repository.

XML schema validation is mandatory in Citrus. This means that Citrus always tries to find a matching schema definition inside the schema repository in order to perform syntax validation on incoming schema qualified XML messages. A classified XML message is defined by its namespace definitions. Consequently you will get validation errors in case no matching schema definition file is found inside the schema repository. So if you explicitly do not want to validate the XML schema for some reason you have to disable the validation explicitly in your test with schema-validation="false".
<receive endpoint="httpMessageEndpoint">
    <message schema-validation="false">
      <validate>
        <xpath expression="//ns1:TestMessage/ns1:MessageHeader/ns1:MessageId"
             value="${messageId}"/>
        <xpath expression="//ns1:TestMessage/ns1:MessageHeader/ns1:CorrelationId"
             value="${correlationId}"/>
        <namespace prefix="ns1" value="http://citrus.com/namespace"/>
      </validate>
    </message>
    <header>
        <element name="Operation" value="sayHello"/>
        <element name="MessageId" value="${messageId}"/>
    </header>
</receive>

This mandatory schema validation might sound annoying to you but in our opinion it is very important to validate the structure of the received XML messages, so disabling the schema validation should not be the standard for all tests. Disabling automatic schema validation should only apply to very special situations. So please try to put all available schema definitions to the schema repository and you will be fine.

10.4.3.2. WSDL schemas

In SOAP WebServices world the WSDL (WebService Schema Definition Language) defines the structure an nature of the XML messages exchanged across the interface. Often the WSDL files do hold the XML schema definitions as nested elements. In Citrus you can directly set the WSDL file as location of a schema definition like this:

<citrus:schema id="arilineWsdl"
    location="classpath:citrus/flightbooking/AirlineSchema.wsdl"/>

Citrus is able to find the nested schema definitions inside the WSDL file in order to build a valid schema file for the schema repository. So incoming XML messages that refer to the WSDL file can be validated for syntax rules.

10.4.3.3. Schema collections

Sometimes a XML schema definition is separated into multiple files. This is a problem for the Citrus schema repository as the schema mapping strategy then is not able to pick the right file for validation, in particular when working with target namespace values as key for the schema mapping strategy. As a solution for this problem you have to put all schemas with the same target namespace value into a schema collection.

<citrus:schema-collection id="flightbookingSchemaCollection">
  <citrus:schemas>
    <citrus:schema location="classpath:citrus/flightbooking/BaseTypes.xsd"/>
    <citrus:schema location="classpath:citrus/flightbooking/AirlineSchema.xsd"/>
  </citrus:schemas>
</citrus:schema-collection>

Both schema definitions BaseTypes.xsd and AirlineSchema.xsd share the same target namespace and therefore need to be combined in schema collection component. The schema collection can be referenced in any schema repository as normal schema definition.

<citrus:schema-repository id="schemaRepository">
  <citrus:schemas>
    <citrus:reference schema="flightbookingSchemaCollection"/>
  </citrus:schemas>
</citrus:schema-repository>
10.4.3.4. Schema mapping strategy

The schema repository in Citrus holds one to many schema definition files and dynamically picks up the right one according to the validated message payload. The repository needs to have some strategy for deciding which schema definition to choose. See the following schema mapping strategies and decide which of them is suitable for you.

10.4.3.5. Target Namespace Mapping Strategy

This is the default schema mapping strategy. Schema definitions usually define some target namespace which is valid for all elements and types inside the schema file. The target namespace is also used as root namespace in XML message payloads. According to this information Citrus can pick up the right schema definition file in the schema repository. You can set the schema mapping strategy as property in the configuration files:

<citrus:schema-repository id="schemaRepository"
    schema-mapping-strategy="schemaMappingStrategy">
  <citrus:schemas>
    <citrus:schema id="helloSchema"
        location="classpath:citrus/samples/sayHello.xsd"/>
  </citrus:schemas>
</citrus:schema-repository>

<bean id="schemaMappingStrategy"
    class="com.consol.citrus.xml.schema.TargetNamespaceSchemaMappingStrategy"/>

The sayHello.xsd schema file defines a target namespace (http://consol.de/schemas/sayHello.xsd):

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns="http://consol.de/schemas/sayHello.xsd"
    targetNamespace="http://consol.de/schemas/sayHello.xsd"
    elementFormDefault="qualified"
    attributeFormDefault="unqualified">

</xs:schema>

Incoming request messages should also have the target namespace set in the root element and this is how Citrus matches the right schema file in the repository.

<HelloRequest xmlns="http://consol.de/schemas/sayHello.xsd">
   <MessageId>123456789</MessageId>
   <CorrelationId>1000</CorrelationId>
   <User>Christoph</User>
   <Text>Hello Citrus</Text>
</HelloRequest>
10.4.3.6. Root QName Mapping Strategy

The next possibility for mapping incoming request messages to a schema definition is via the XML root element QName. Each XML message payload starts with a root element that usually declares the type of a XML message. According to this root element you can set up mappings in the schema repository.

<citrus:schema-repository id="schemaRepository"
    schema-mapping-strategy="schemaMappingStrategy">
  <citrus:schemas>
    <citrus:reference schema="helloSchema"/>
    <citrus:reference schema="goodbyeSchema"/>
  </citrus:schemas>
</citrus:schema-repository>

<bean id="schemaMappingStrategy"
    class="com.consol.citrus.xml.schema.RootQNameSchemaMappingStrategy">
  <property name="mappings">
    <map>
      <entry key="HelloRequest" value="helloSchema"/>
      <entry key="GoodbyeRequest" value="goodbyeSchema"/>
    </map>
  </property>
</bean>

<citrus:schema id="helloSchema"
    location="classpath:citrus/samples/sayHello.xsd"/>

<citrus:schema id="goodbyeSchema"
     location="classpath:citrus/samples/sayGoodbye.xsd"/>

The listing above defines two root qname mappings - one for HelloRequest and one for GoodbyeRequest message types. An incoming message of type <HelloRequest> is then mapped to the respective schema and so on. With this dedicated mappings you are able to control which schema is used on a XML request, regardless of target namespace definitions.

10.4.3.7. Schema mapping strategy chain

Let’s discuss the possibility to combine several schema mapping strategies in a logical chain. You can define more than one mapping strategy that are evaluated in sequence. The first strategy to find a proper schema definition file in the repository wins.

<citrus:schema-repository id="schemaRepository"
    schema-mapping-strategy="schemaMappingStrategy">
  <citrus:schemas>
    <citrus:reference schema="helloSchema"/>
    <citrus:reference schema="goodbyeSchema"/>
  </citrus:schemas>
</citrus:schema-repository>

<bean id="schemaMappingStrategy"
    class="com.consol.citrus.xml.schema.SchemaMappingStrategyChain">
  <property name="strategies">
    <list>
      <bean class="com.consol.citrus.xml.schema.RootQNameSchemaMappingStrategy">
        <property name="mappings">
          <map>
            <entry key="HelloRequest" value="helloSchema"/>
          </map>
        </property>
      </bean>
      <bean class="com.consol.citrus.xml.schema.TargetNamespaceSchemaMappingStrategy"/>
    </list>
  </property>
</bean>

So the schema mapping chain uses both RootQNameSchemaMappingStrategy and TargetNamespaceSchemaMappingStrategy in combination. In case the first root qname strategy fails to find a proper mapping the next target namespace strategy comes in and tries to find a proper schema.

10.4.3.8. DTD validation

XML DTD (document type definition) is another way to validate the structure of a XML document. Many people say that DTD is deprecated and XML schema is the much more efficient way to describe the rules of a XML structure. We do not disagree with that, but we also know that legacy systems might still use DTD. So in order to avoid validation errors we have to deal with DTD validation as well.

First thing you can do about DTD validation is to specify an inline DTD in your expected message template.

<receive endpoint="httpMessageEndpoint">
    <message schema-validation="false">
        <data>
        <![CDATA[
            <!DOCTYPE root [
                <!ELEMENT root (message)>
                <!ELEMENT message (text)>
                <!ELEMENT text (#PCDATA)>
                ]>
            <root>
                <message>
                    <text>Hello TestFramework!</text>
                </message>
            </root>
        ]]>
        <data/>
    </message>
</receive>

The system under test may also send the message with a inline DTD definition. So validation will succeed.

In most cases the DTD is referenced as external .dtd file resource. You can do this in your expected message template as well.

<receive endpoint="httpMessageEndpoint">
    <message schema-validation="false">
        <data>
        <![CDATA[
            <!DOCTYPE root SYSTEM
                         "com/consol/citrus/validation/example.dtd">
            <root>
                <message>
                    <text>Hello TestFramework!</text>
                </message>
            </root>
        ]]>
        <data/>
    </message>
</receive>

10.4.4. JSON schema validation

The JSON schema validation in Citrus is based on the drafts provided by json-schema.org. Because JSON schema is a fast evolving project, only JSON schema V3 and V4 are currently supported.

In contrast to the XML validation, the JSON validation is an optional feature. You have to activate it withing every receive-message action by setting schema-validation="true"
<http:receive-request server="echoHttpServer">
    <http:POST>
      <http:body type="json" schema="bookStore" schema-validation="true">
        <http:data>
          {
            "isbn" : "0345391802",
            "title": "The Hitchhiker's Guide to the Galaxy",
            "author": "Douglas Adams"
          }
        </http:data>
      </http:body>
    </http:POST>
</http:receive-request>

This behavior is currently required, to provide downwards compatibility to previous Citrus versions, because a mandatory validation would cause all previous JSON based test cases to fail, due to the missing JSON schemas for the messages within the test. That would have forced you to update all your JSON tests with the proper schema files. This led us the decision to add the JSON validation as an optional feature initially. Nevertheless we encourage you to add JSON schema validation to your test cases as soon as possible, because we think that message validation is a important part of integration testing.

10.4.5. JSON schema repositories

Because Citrus supports different types of schema repositories, it is necessary to declare a JSON schema repository as type="json". This allows Citrus to collect all JSON schema files for the message validation.

<citrus:schema-repository type="json" id="jsonSchemaRepository">
    <citrus:schemas>
        <citrus:schema id="product" location="classpath:com/consol/citrus/validation/ProductsSchema.json"/>
    </citrus:schemas>
</citrus:schema-repository>

10.4.6. JSON schema filtering and validation strategy

In reference to the current JSON schema definition, it is not possible to create a direct reference between a JSON message and a set of schemas, as it would be possible with XML namespaces. Because of that, Citrus follows a rule set for choosing the relevant schemas based on the configuration withing the test case in relation to the given context. The following table assumes that the JSON schema validation is activated for the test action.

Scenario Validation rules

No JSON schema repositories are defined in the context.

No JSON schema validation applies.

There is at least one JSON schema repository defined in the context.

The message of the test action must be valid regarding at least one of the available schemas within the context.

A schema overruling is configured in the test case.

The configured schema must exist and the message must be valid regarding to the specified schema.

A schema repository overruling is configured in the test case.

The configured schema repository must exist and the message must be valid regarding at least one of the schemas within the specified schema repository.

10.5. XHTML message validation

When Citrus receives plain Html messages we likely want to use the powerful XML validation capabilities such as XML tree comparison or XPath support. Unfortunately Html messages do not follow the XML well formed rules very strictly. This implies that XML message validation will fail because of non well formed Html code.

XHTML closes this gap by automatically fixing the most common Html XML incompatible rule violations such as missing end tags (e.g. <br>).

Let’s try this with a simple example. Very first thing for us to do is to add a new library dependency to the project. Citrus is using the jtidy library in order to prepare the HTML and XHTML messages for validation. As this 3rd party dependency is optional in Citrus we have to add it now to our project dependency list. Just add the jtidy dependency to your Maven project POM.

<dependency>
    <groupId>net.sf.jtidy</groupId>
    <artifactId>jtidy</artifactId>
    <version>r938</version>
  </dependency>

Please refer to the jtidy project documentation for the latest versions. Now everything is ready. As usual the Citrus message validator for XHTML is active in background by default. You can overwrite this default implementation by placing a Spring bean with id defaultXhtmlMessageValidator to the Citrus application context.

<bean id="defaultXhtmlMessageValidator" class="com.consol.citrus.validation.xhtml.XhtmlMessageValidator"/>

Now we can tell the test case receiving action that we want to use the XHTML message validation in our test case.

<receive endpoint="httpMessageEndpoint">
    <message type="xhtml">
        <data>
          <![CDATA[
            <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "org/w3c/xhtml/xhtml1-strict.dtd">
            <html xmlns="http://www.w3.org/1999/xhtml">
            <head>
              <title>Citrus Hello World</title>
            </head>
            <body>
              <h1>Hello World!</h1>
              <br/>
              <p>This is a test!</p>
            </body>
          ]]>
        </data>
    </message>
</receive>

The message receiving action in our test case has to specify a message format type type="xhtml" . As you can see the Html message payload get XHTML specific DOCTYPE processing instruction. The xhtml1-strict.dtd is mandatory in the XHTML message validation. For better convenience all XHTML dtd files are packaged within Citrus so you can use this as a relative path.

The incoming Html message is automatically converted into proper XHTML code with well formed XML. So now the XHTML message validator can use the XML message validation mechanism of Citrus for comparing received and expected data. As usual you can use test variables, ignore element expressions and XPath expressions.

10.6. Plain text message validation

Plain text message validation is the easiest validation in Citrus that you can think of. This validation just performs an exact Java String match of received and expected message payloads.

As usual a default message validator for plaintext messages is active by default. Citrus will pick this message validator for all messages of type="plaintext" . The default message validator implementation can be overwritten by placing a Spring bean with id defaultPlaintextMessageValidator to the Spring application context.

<bean id="defaultPlaintextMessageValidator" class="com.consol.citrus.validation.text.PlainTextMessageValidator"/>

In the test case receiving action we tell Citrus to use plain text message validation.

<receive endpoint="httpMessageEndpoint">
    <message type="plaintext">
        <data>Hello World!</data>
    </message>
</receive>

With the message format type type="plaintext" set Citrus performs String equals on the message payloads (received and expected). Only exact match will pass the test.

By the way sending plain text messages in Citrus is also very easy. Just use the plain text message payload data in your sending message action.

<send endpoint="httpMessageEndpoint">
    <message>
        <data>Hello World!</data>
    </message>
</send>

Of course test variables are supported in the plain text payloads. The variables are replace by the referenced values before sending or receiving the message.

<receive endpoint="httpMessageEndpoint">
    <message type="plaintext">
        <data>${hello} ${world}!</data>
    </message>
</receive>

10.6.1. Whitespace characters

Plaintext message payloads may only differ in system-dependent line separator characters (CR, LF, CRLF). By default the plain text message validation fails because of that differences even if only whitespace characters are different.

You can disable this default validation behavior and ignore new line types with following system property or environment variable:

citrus.plaintext.validation.ignore.newline.type=true
CITRUS_PLAINTEXT_VALIDATION_IGNORE_NEWLINE_TYPE=true

In case you need to ignore all whitespaces during plain text validation such as multiple new line characters or tabs you need to set this system property or environment variable:

citrus.plaintext.validation.ignore.whitespace=true
CITRUS_PLAINTEXT_VALIDATION_IGNORE_WHITESPACE=true

This property will not only ignore new line types but also normalize the whitespaces. As a result all empty lines, tabs and double whitespace characters are filtered before comparison.

Of course you can also set the properties directly on the plain text message validator bean:

<bean id="defaultPlaintextMessageValidator" class="com.consol.citrus.validation.text.PlainTextMessageValidator">
  <property name="ignoreNewLineType" value="true"/>
  <property name="ignoreWhitespace" value="true"/>
</bean>

10.6.2. Ignoring text parts

By default the plaint text validator performs a String equals operation. Test variables are automatically replaced before that comparison takes place but what about ignore statements? The plain text message validator is able to ignore words and character sequences based on their position in the text value. Given a source plain text value:

Source text
Your current id is "1234567890"

Now in the plain text validation we need to ignore the actual id value due to some reason. Maybe the id is generated on a foreign system and we simply do not know the actual value at runtime. In this case we can use the common @ignore@ statement in the control message payload as follows:

Control text
Your current id is "@ignore@"

Citrus and the plain text message validator will ignore the marked part of the text during validation. This mechanism is based on the fact that the @ignore@ statement is placed at the exact same position as the actual id value. So this mechanism requires you to know the exact structure of the plaintext value including all whitespace characters. When Citrus finds the @ignore@ keyword in the control value the placeholder is replaced with the actual character sequence that is located at the exact same position in the source message payload that is validated.

The character sequence is defined as sequence of Java word characters. This word sequence is ending with a non-word character defined in Java (\\W which is a character that is not in [a-zA-Z_0-9]).

Instead of ignoring a single word you can also specify the amount of characters that should be ignored. This is when you have Java non-word characters that you need to ignore. Let’s have an example for that, too:

Source text
Your current id is "#12345-67890"

Given that text the simple @ignore@ statement will fail because of the non-word characters '#' and '-' that are located in the id value. This time we ignore the whole id sequence with:

Control text
Your current id is "@ignore(12)@"

This will ignore exactly 12 characters starting from the exact position of the @ignore@ keyword. So knowing that the id is exactly 12 characters long we can ignore that part.

10.6.3. Creating variables

Instead of just ignoring certain text parts we can also extract those parts into test variables. The actual character sequence is ignored during validation and in addition to that the actual value is stored to a new test variable. Given the following text payload:

Source text
Your current id is "1234567890"

And the expected control text:

Control text
Your current id is "@variable('id')@"

The validation will automatically ignore the id part in the text and create a new test variable with name id that holds the actual value. The name of the variable to create is given in the @variable()@ statement. This enables us to extract dynamic text parts that we are not able to validate. After that we can access the dynamic text part using the normal test variable syntax:

The id was ${id}

Also notice that the @variable()@ keyword expression has to be placed at the exact same position in the text as the actual value. The variable extractor will read the variable value from the source message payload starting from that position. The ending of the variable value is defined by a non-word Java character. Dashes '-' and dots '.' are automatically included in these values, too. So this will also work for you:

Source text
Today is "2017-12-24"

And the expected control text:

Control text
Today is "@variable('date')@"

This results in a new variable called date with value 2017-12-24. Also the European date representation works fine here as dots and dashes are automatically included:

Source text
Today is "24.12.2017"

10.7. Binary message validation

Binary message validation is not very easy to do especially when it comes to compare data with a given control message. As a tester you want to validate the binary content. In Citrus the way to compare binary message content is to use base64 String encoding. The binary data is encoded as base64 character sequence and there fore is comparable with an expected content.

The received message content does not have to be base64 encoded. Citrus is doing this conversion automatically before validation takes place. The binary data can be anything e.g. images, pdf or gzip content.

The default message validator for binary messages is active by default. Citrus will pick this message validator for all messages of type="binary_base64" . The default message validator implementation can be overwritten by placing a Spring bean with id defaultBinaryBase64MessageValidator to the Spring application context.

<bean id="defaultBinaryBase64MessageValidator" class="com.consol.citrus.validation.text.BinaryBase64MessageValidator"/>

In the test case receiving action we tell Citrus to use binary base64 message validation.

<receive endpoint="httpMessageEndpoint">
    <message type="binary_base64">
        <data>citrus:encodeBase64('Hello World!')</data>
    </message>
</receive>

With the message format type type="binary_base64" Citrus performs the base64 character sequence validation. Incoming message content is automatically encoded as base64 String and compared to the expected data. This way we can make sure that the binary content is as expected.

By the way sending binary messages in Citrus is also very easy. Just use the type="binary" message type in the send operation. Citrus now converts the message payload to a binary stream as payload.

<send endpoint="httpMessageEndpoint">
    <message type="binary">
        <data>Hello World!</data>
    </message>
</send>

Base64 encoding is also supported in outbound messages. Just use the encodeBase64 function in Citrus. The result is a base64 encoded String as message payload.

<send endpoint="httpMessageEndpoint">
    <message>
        <data>citrus:encodeBase64('Hello World!')</data>
    </message>
</send>

10.8. Gzip message validation

Gzip is a famous message compression library. When dealing with large message content the compression might be a good way to optimize the message transportation. Citrus is able to handle gzipped message payloads on send and receive operations. When sending compressed data we just have to use the message type gzip.

<send endpoint="messageEndpoint">
    <message type="gzip">
        <data>Hello World!</data>
    </message>
</send>

Just use the type="gzip" message type in the send operation. Citrus now converts the message payload to a gzip binary stream as payload.

When validating gzip binary message content the messages are compared with a given control message in binary base64 String representation. The gzip binary data is automatically unzipped and encoded as base64 character sequence in order to compare with an expected content.

The received message content is using gzip format but the actual message content does not have to be base64 encoded. Citrus is doing this conversion automatically before validation takes place. The binary data can be anything e.g. images, pdf or plaintext content.

The default message validator for gzip messages is active by default. Citrus will pick this message validator for all messages of type="gzip_base64" . The default message validator implementation can be overwritten by placing a Spring bean with id defaultGzipBinaryBase64MessageValidator to the Spring application context.

<bean id="defaultGzipBinaryBase64MessageValidator" class="com.consol.citrus.validation.text.GzipBinaryBase64MessageValidator"/>

In the test case receiving action we tell Citrus to use gzip message validation.

<receive endpoint="messageEndpoint">
    <message type="gzip_base64">
        <data>citrus:encodeBase64('Hello World!')</data>
    </message>
</receive>

With the message format type type="gzip_base64" Citrus performs the gzip base64 character sequence validation. Incoming message content is automatically unzipped and encoded as base64 String and compared to the expected data. This way we can make sure that the binary content is as expected.

If you are using http client and server components the gzip compression support is built in with the underlying Spring and http commons libraries. So in http communication you just have to set the header Accept-Encoding=gzip or Content-Encoding=gzip. The message data is then automatically zipped/unzipped before Citrus gets the message data for validation. Read more about this http specific gzip compression in chapter http.

10.9. Java DSL validation callbacks

The Java DSL offers some additional validation tricks and possibilities when dealing with messages that are sent and received over Citrus. One of them is the validation callback functionality. With this feature you can marshal/unmarshal message payloads and code validation steps on Java objects.

Java DSL designer
@CitrusTest
public void receiveMessageTest() {
    receive(bookResponseEndpoint)
        .validationCallback(new XmlMarshallingValidationCallback<AddBookResponseMessage>() {
            @Override
            public void validate(AddBookResponseMessage response, MessageHeaders headers) {
                Assert.isTrue(response.isSuccess());
            }
        });
}

By default the validation callback needs some XML unmarshaller implementation for transforming the XML payload to a Java object. Citrus will automatically search for the unmarshaller bean in your Spring application context if nothing specific is set. Of course you can also set the unmarshaller instance explicitly.

Java DSL designer
@Autowired
private Unmarshaller unmarshaller;

@CitrusTest
public void receiveMessageTest() {
    receive(bookResponseEndpoint)
        .validationCallback(new MarshallingValidationCallback<AddBookResponseMessage>(unmarshaller) {
            @Override
            public void validate(AddBookResponseMessage response, MessageHeaders headers) {
                Assert.isTrue(response.isSuccess());
            }
        });
}

Obviously working on Java objects is much more comfortable than using the XML String concatenation. This is why you can also use this feature when sending messages.

Java DSL designer
@Autowired
private Marshaller marshaller;

@CitrusTest
public void sendMessageTest() {
    send(bookRequestEndpoint)
        .payload(createAddBookRequestMessage("978-citrus:randomNumber(10)"), marshaller)
        .header(SoapMessageHeaders.SOAP_ACTION, "addBook");
}

private AddBookRequestMessage createAddBookRequestMessage(String isbn) {
    AddBookRequestMessage requestMessage = new AddBookRequestMessage();
    Book book = new Book();
    book.setAuthor("Foo");
    book.setTitle("FooTitle");
    book.setIsbn(isbn);
    book.setYear(2008);
    book.setRegistrationDate(Calendar.getInstance());
    requestMessage.setBook(book);
    return requestMessage;
}

The example above creates a AddBookRequestMessage object and puts this as payload to a send action. In combination with a marshaller instance Citrus is able to create a proper XML message payload then.

10.10. Customize message validators

In the previous sections we have already seen some examples on how to overwrite default message validator implementations in Citrus. By default all message validators can be overwritten by placing a Spring bean of the same id to the Spring application context. The default implementations of Citrus are:

defaultXmlMessageValidator

com.consol.citrus.validation.xml.DomXmlMessageValidator

defaultXpathMessageValidator

com.consol.citrus.validation.xml.XpathMessageValidator

defaultJsonMessageValidator

com.consol.citrus.validation.json.JsonTextMessageValidator

defaultJsonPathMessageValidator

com.consol.citrus.validation.json.JsonPathMessageValidator

defaultPlaintextMessageValidator

com.consol.citrus.validation.text.PlainTextMessageValidator

defaultMessageHeaderValidator

com.consol.citrus.validation.DefaultMessageHeaderValidator

defaultBinaryBase64MessageValidator

com.consol.citrus.validation.text.BinaryBase64MessageValidator

defaultGzipBinaryBase64MessageValidator

com.consol.citrus.validation.text.GzipBinaryBase64MessageValidator

defaultXhtmlMessageValidator

com.consol.citrus.validation.xhtml.XhtmlMessageValidator

defaultGroovyXmlMessageValidator

com.consol.citrus.validation.script.GroovyXmlMessageValidator

defaultGroovyTextMessageValidator

com.consol.citrus.validation.script.GroovyScriptMessageValidator

defaultGroovyJsonMessageValidator

com.consol.citrus.validation.script.GroovyJsonMessageValidator

Overwriting a single message validator with a custom implementation is then very easy. Just add your custom Spring bean to the application context using one of these default bean identifiers. In case you want to change the message validator gang by adding or removing a message validator implementation completely you can place a message validator component in the Spring application context.

<citrus:message-validators>
    <citrus:validator ref="defaultXmlMessageValidator"/>
    <citrus:validator ref="defaultXpathMessageValidator"/>
    <citrus:validator ref="defaultGroovyXmlMessageValidator"/>
    <citrus:validator ref="defaultPlaintextMessageValidator"/>
    <citrus:validator ref="defaultMessageHeaderValidator"/>
    <citrus:validator ref="defaultBinaryBase64MessageValidator"/>
    <citrus:validator ref="defaultGzipBinaryBase64MessageValidator"/>
    <citrus:validator class="com.consol.citrus.validation.custom.CustomMessageValidator"/>
    <citrus:validator ref="defaultJsonMessageValidator"/>
    <citrus:validator ref="defaultJsonPathMessageValidator"/>
    <citrus:validator ref="defaultGroovyJsonMessageValidator"/>
    <citrus:validator ref="defaultGroovyTextMessageValidator"/>
    <citrus:validator ref="defaultXhtmlMessageValidator"/>
</citrus:message-validators>

The listing above adds a custom message validator implementation to the sequence of message validators in Citrus. We reference default message validators and add a implementation of type com.consol.citrus.validation.custom.CustomMessageValidator . The custom implementation class has to implement the basic interface com.consol.citrus.validation.MessageValidator . Now Citrus will try to match the custom implementation to incoming message types and occasionally execute the message validator logic. This is how you can add and change the basic message validator registry in Citrus. You can add custom implementations for new message formats very easy.

The same approach applies in case you want to remove a message validator implementation by banning it completely. Just delete the entry in the message validator registry component:

<citrus:message-validators>
    <citrus:validator ref="defaultJsonMessageValidator"/>
    <citrus:validator ref="defaultJsonPathMessageValidator"/>
    <citrus:validator ref="defaultGroovyJsonMessageValidator"/>
    <citrus:validator ref="defaultGroovyTextMessageValidator"/>
    <citrus:validator ref="defaultMessageHeaderValidator"/>
</citrus:message-validators>

The Citrus message validator component deleted all default implementations except of those dealing with JSON message format. Now Citrus is only able to validate JSON messages. Be careful as the complete Citrus project will be affected by this change.

11. Using XPath

Some time ago in this document we have already seen how XML message payloads are constructed when sending and receiving messages. Now using XPath is a very powerful way of accessing elements in complex XML structures. The XPath expression language is very handy when it comes to save element values as test variables or when validating special elements in a XML message structure.

XPath is a very powerful technology for walking XML trees. This W3C standard stands for advanced XML tree handling using a special syntax as query language. Citrus supports the XPath syntax in the following fields:

message

<message><element path="[XPath-Expression]"></message>

validate

<validate><xpath expression="[XPath-Expression]"/></validate>

extract

<extract><message path="[XPath-Expression]"></extract>

ignore

<ignore path="[XPath-Expression]"/>

The next program listing indicates the power in using XPath with Citrus:

<message>
  <validate>
    <xpath expression="//User/Name" value="John"/>
    <xpath expression="//User/Address[@type='office']/Street" value="Companystreet 21"/>
    <xpath expression="//User/Name" value="${userName}"/>
    <xpath expression="//User/@isAdmin" value="${isAdmin}"/>
    <xpath expression="//User/@isAdmin" value="true" result-type="boolean"/>
    <xpath expression="//*[.='search-for']" value="searched-for"/>
    <xpath expression="count(//orderStatus[.='success'])" value="3" result-type="number"/>
  </validate>
</message>

Now we describe the XPath usage in Citrus step by step.

11.1. Manipulate with XPath

Some elements in XML message payloads might be of dynamic nature. Just think of generated identifiers or timestamps. Also we do not want to repeat the same static identifier several times in our test cases. This is the time where test variables and dynamic message element overwrite come in handy. The idea is simple. We want to overwrite a specific message element in our payload with a dynamic value. This can be done with XPath or inline variable declarations. Lets have a look at an example listing showing both ways:

XML DSL
<message>
  <payload>
    <TestMessage>
      <MessageId>${messageId}</MessageId>
      <CreatedBy>_</CreatedBy>
      <VersionId>${version}</VersionId>
    </TestMessage>
  </payload>
  <element path="/TestMessage/CreatedBy" value="${user}"/>
</message>

The program listing above shows ways of setting variable values inside a message template. First of all you can simply place variable expressions inside the message (see how ${messageId} is used). In addition to that you can also use XPath expressions to explicitly overwrite message elements before validation.

<element path="/TestMessage/CreatedBy" value="${user}"/>

The XPath expression evaluates and searches for the right element in the message payload. The previously defined variable ${user} replaces the element value. Of course this works with XML attributes too.

Both ways via XPath or inline variable expressions are equal to each other. With respect to the complexity of XML namespaces and XPath you may find the inline variable expression more comfortable to use. Anyway feel free to choose the way that fits best for you. This is how we can add dynamic variable values to the control template in order to increase maintainability and robustness of message validation.

Validation matchers put validation mechanisms to a new level offering dynamic assertion statements for validation. Have a look at the possibilities with assertion statements in validation-matcher.

11.2. Validate with XPath

We have already seen how to validate whole XML structures with control message templates. All elements are validated and compared one after another. In some cases this approach might be too extensive. Imagine the tester only needs to validate a small subset of message elements. The definition of control templates in combination with several ignore statements is not appropriate in this case. You would rather want to use explicit element validation.

XML DSL
<message>
  <validate>
    <xpath expression="/TestRequest/MessageId" value="${messageId}"/>
    <xpath expression="/TestRequest/VersionId" value="2"/>
  </validate>
</message>
Java DSL designer
@CitrusTest
public void receiveMessageTest() {
    receive("helloServiceServer")
        .validate("/TestRequest/MessageId", "${messageId}")
        .validate("//VersionId", "2")
        .header("Operation", "sayHello");
}

Instead of comparing the whole message some message elements are validated explicitly via XPath. Citrus evaluates the XPath expression on the received message and compares the result value to the control value. The basic message structure as well as all other message elements are not included into this explicit validation.

If this type of element validation is chosen neither <payload> nor <data> nor <resource> template definitions are allowed in Citrus XML test cases.
Citrus offers an alternative dot-notated syntax in order to walk through XML trees. In case you are not familiar with XPath or simply need a very easy way to find your element inside the XML tree you might use this way. Every element hierarchy in the XML tree is represented with a simple dot - for example:

TestRequest.VersionId

The expression will search the XML tree for the respective <TestRequest><VersionId> element. Attributes are supported too. In case the last element in the dot-notated expression is a XML attribute the framework will automatically find it.

Of course this dot-notated syntax is very simple and might not be applicable for more complex tree navigation. XPath is much more powerful - no doubt. However the dot-notated syntax might help those of you that are not familiar with XPath. So the dot-notation is supported wherever XPath expressions might apply.

The Xpath expressions can evaluate to different result types. By default Citrus is operating on NODE and STRING result types so that you can validate some element value. But you can also use different result types such as NODESET and BOOLEAN . See this example how that works:

XML DSL
<message>
  <validate>
    <xpath expression="/TestRequest/Error" value="false" result-type="boolean"/>
    <xpath expression="/TestRequest/Status[.='success']" value="3" result-type="number"/>
    <xpath expression="/TestRequest/OrderType" value="[single, multi, multi]" result-type="node-set"/>
  </validate>
</message>
Java DSL designer
@CitrusTest
public void receiveMessageTest() {
    receive("helloServiceServer")
        .validate("boolean:/TestRequest/Error", false)
        .validate("number:/TestRequest/Status[.='success']", 3)
        .validate("node-set:/TestRequest/OrderType", "[single, multi, multi]")
        .header("Operation", "sayHello");
}

In the example above we use different expression result types. First we want to make sure nor /TestRequest/Error element is present. This can be done with a boolean result type and false value. Second we want to validate the number of found elements for the expression /TestRequest/Status[.='success'] . The XPath expression evaluates to a node list that results in its list size to be checked. And last not least we evaluate to a node-set result type where all values in the node list will be translated to a comma delimited string value.

Now lets have a look at some more powerful validation expressions using matcher implementations. Up to now we have seen that XPath expression results are comparable with equalTo operations. We would like to add some more powerful validation such as greaterThan, lessThan, hasSize and much more. Therefore we have introduced Hamcrest validation matcher support in Citrus. Hamcrest is a very powerful matcher library that provides a fantastic set of matcher implementations. Lets see how we can add these in our test case:

XML DSL
<message>
  <validate>
    <xpath expression="/TestRequest/Error" value="@assertThat(anyOf(empty(), nullValue()))@"/>
    <xpath expression="/TestRequest/Status[.='success']" value="@assertThat(greaterThan(0.0))@" result-type="number"/>
    <xpath expression="/TestRequest/Status[.='failed']" value="@assertThat(lowerThan(1))@" result-type="integer"/>
    <xpath expression="/TestRequest/OrderType" value="@assertThat(hasSize(3))@" result-type="node-set"/>
  </validate>
</message>
Java DSL designer
@CitrusTest
public void receiveMessageTest() {
    receive("helloServiceServer")
        .validate("/TestRequest/Error", anyOf(empty(), nullValue()))
        .validate("number:/TestRequest/Status[.='success']", greaterThan(0.0))
        .validate("integer:/TestRequest/Status[.='failed']", lowerThan(1))
        .validate("node-set:/TestRequest/OrderType", hasSize(3))
        .header("Operation", "sayHello");
}
XPath uses decimal number type Double by default when evaluating expressions with number result type. This means we have to use Double typed expected values, too. Citrus also provides the result type integer that automatically converts the XPath expression result to a Integer type.

When using the XML DSL we have to use the assertThat validation matcher syntax for defining the Hamcrest matcher. You can combine matcher implementation as seen in the anyOf(empty(), nullValue()) expression. When using the Java DSL you can just add the matcher as expected result object. Citrus evaluates the matchers and makes sure everything is as expected. This is a very powerful validation mechanism as it also works with node-sets containing multiple values as list.

This is how you can add very powerful message element validation in XML using XPath expressions.

11.3. Extract variables with XPath

Imagine you receive a message in your test with some generated message identifier values. You have no chance to predict the identifier value because it was generated at runtime by a foreign application. You can ignore the value in order to protect your validation. But in many cases you might need to return this identifier in the respective response message or somewhat later on in the test. So we have to save the dynamic message content for reuse in later test steps. The solution is simple and very powerful. We can extract dynamic values from received messages and save those to test variables. Add this code to your message receiving action.

XML DSL
<extract>
  <header name="Operation" variable="operation"/>
  <message path="/TestRequest/VersionId" variable="versionId"/>
</extract>
Java DSL designer
@CitrusTest
public void receiveMessageTest() {
    receive("helloServiceServer")
        .extractFromHeader("Operation", "operation")
        .extractFromPayload("//TestRequest/VersionId", "versionId");

    echo("Extracted operation from header is: ${operation}");
    echo("Extracted version from payload is: ${versionId}");
}

As you can see Citrus is able to extract both header and message payload content into test variables. It does not matter if you use new test variables or existing variables as target. The extraction will automatically create a new variable in case it does not exist. The time the variable was created all following test actions can access the test variables as usual. So you can reference the variable values in response messages or other test steps ahead.

We can also use expression result types in order to manipulate the test variable outcome. In case we use a boolean result type the existence of elements can be saved to variable values. The result type node-set translates a node list result to a comma separated string of all values in this node list. Simply use the expression result type attributes as shown in previous sections.

11.4. XML namespaces in XPath

When it comes to XML namespaces you have to be careful with your XPath expressions. Lets have a look at an example message that uses XML namespaces:

<ns1:TestMessage xmlns:ns1="http://citrus.com/namespace">
    <ns1:TestHeader>
        <ns1:CorrelationId>_</ns1:CorrelationId>
        <ns1:Timestamp>2001-12-17T09:30:47.0Z</ns1:Timestamp>
        <ns1:VersionId>2</ns1:VersionId>
    </ns1:TestHeader>
    <ns1:TestBody>
        <ns1:Customer>
            <ns1:Id>1</ns1:Id>
        </ns1:Customer>
    </ns1:TestBody>
</ns1:TestMessage>

Now we would like to validate some elements in this message using XPath

<message>
  <validate>
    <xpath expression="//TestMessage/TestHeader/VersionId" value="2"/>
    <xpath expression="//TestMessage/TestHeader/CorrelationId" value="${correlationId}"/>
  </validate>
</message>

The validation will fail although the XPath expression looks correct regarding the XML tree. Because the message uses the namespace with its prefix ns1 our XPath expression is not able to find the elements. The correct XPath expression uses the namespace prefix as defined in the message.

<message>
  <validate>
    <xpath expression="//ns1:TestMessage/ns1:TestHeader/ns1:VersionId" value="2"/>
    <xpath expression="//ns1:TestMessage/ns1:TestHeader/ns1:CorrelationId" value="${correlationId}"/>
</message>

Now the expressions work fine and the validation is successful. But this is quite error prone. This is because the test is now depending on the namespace prefix that is used by some application. As soon as the message is sent with a different namespace prefix (e.g. ns2) the validation will fail again.

You can avoid this effect when specifying your own namespace context and your own namespace prefix during validation.

<message>
  <validate>
    <xpath expression="//pfx:TestMessage/pfx:TestHeader/pfx:VersionId" value="2"/>
    <xpath expression="//pfx:TestMessage/pfx:TestHeader/pfx:CorrelationId" value="${correlationId}"/>
    <namespace prefix="pfx" value="http://citrus.com/namespace"/>
  </validate>
</message>

Now the test in independent from any namespace prefix in the received message. The namespace context will resolve the namespaces and find the elements although the message might use different prefixes. The only thing that matters is that the namespace value (http://citrus.com/namespace) matches.

Instead of this namespace context on validation level you can also have a global namespace context which is valid in all test cases. We just add a bean in the basic Spring application context configuration which defines global namespace mappings.
<namespace-context>
    <namespace prefix="def" uri="http://www.consol.de/samples/sayHello"/>
</namespace-context>

Once defined the def namespace prefix is valid in all test cases and all XPath expressions. This enables you to free your test cases from namespace prefix bindings that might be broken with time. You can use these global namespace mappings wherever XPath expressions are valid inside a test case (validation, ignore, extract).

11.5. Default namespaces in XPath

In the previous section we have seen that XML namespaces can get tricky with XPath validation. Default namespaces can do even more! So lets look at the example with default namespaces:

<TestMessage xmlns="http://citrus.com/namespace">
    <TestHeader>
        <CorrelationId>_</CorrelationId>
        <Timestamp>2001-12-17T09:30:47.0Z</Timestamp>
        <VersionId>2</VersionId>
    </TestHeader>
    <TestBody>
        <Customer>
            <Id>1</Id>
        </Customer>
    </TestBody>
</TestMessage>

The message uses default namespaces. The following approach in XPath will fail due to namespace problems.

<message>
  <validate>
    <xpath expression="//TestMessage/TestHeader/VersionId" value="2"/>
    <xpath expression="//TestMessage/TestHeader/CorrelationId" value="${correlationId}"/>
  </validate>
</message>

Even default namespaces need to be specified in the XPath expressions. Look at the following code listing that works fine with default namespaces:

<message>
  <validate>
    <xpath expression="//:TestMessage/:TestHeader/:VersionId" value="2"/>
    <xpath expression="//:TestMessage/:TestHeader/:CorrelationId" value="${correlationId}"/>
  </validate>
</message>
It is recommended to use the namespace context as described in the previous chapter when validating. Only this approach ensures flexibility and stable test cases regarding namespace changes.

12. Using JSONPath

JSONPath is the JSON equivalent to XPath in the XML message world. With JSONPath expressions you can query and manipulate entries of a JSON message structure. The JSONPath expressions evaluate against a JSON message where the JSON object structure is represented in a dot notated syntax.

You will see that JSONPath is a very powerful technology when it comes to find object entries in a complex JSON hierarchy structure. Also JSONPath can help to do message manipulations before a message is sent out for instance. Citrus supports JSONPath expressions in various scenarios:

message

<message><element path="[JSONPath-Expression]"></message>

validate

<validate><json-path expression="[JSONPath-Expression]"/></validate>

extract

<extract><message path="[JSONPath-Expression]"></extract>

ignore

<ignore path="[JSONPath-Expression]"/>

12.1. Manipulate with JSONPath

First thing we want to do with JSONPath is to manipulate a message content before it is actually sent out. This is very useful when working with message file resources that are reused across multiple test cases. Each test case can manipulate the message content individually with JSONPath before sending. Lets have a look at this simple sample:

<message type="json">
  <resource file="file:path/to/user.json" />
  <element path="$.user.name" value="Admin" />
  <element path="$.user.admin" value="true" />
  <element path="$..status" value="closed" />
</message>

We use a basic message content file that is called user.json . The content of the file is following JSON data structure:

{ "user":
  {
    "id": citrus:randomNumber(10),
    "name": "Unknown",
    "admin": "?",
    "projects":
      [{
        "name": "Project1",
        "status": "open"
      },
      {
        "name": "Project2",
        "status": "open"
      },
      {
        "name": "Project3",
        "status": "closed"
      }]
  }
}

Citrus loads the file content and used it as message payload. Before the message is sent out the JSONPath expressions have the chance to manipulate the message content. All JSONPath expressions are evaluated and the give values overwrite existing values accordingly. The resulting message looks like follows:

{ "user":
  {
    "id": citrus:randomNumber(10),
    "name": "Admin",
    "admin": "true",
    "projects":
      [{
        "name": "Project1",
        "status": "closed"
      },
      {
        "name": "Project2",
        "status": "closed"
      },
      {
        "name": "Project3",
        "status": "closed"
      }]
  }
}

The JSONPath expressions have set the user name to Admin . The admin boolean property was set to true and all project status values were set to closed . Now the message is ready to be sent out. In case a JSONPath expression should fail to find a matching element within the message structure the test case will fail.

With this JSONPath mechanism ou are able to manipulate message content before it is sent or received within Citrus. This makes life very easy when using message resource files that are reused across multiple test cases.

12.2. Validate with JSONPath

Lets continue to use JSONPath expressions when validating a receive message in Citrus:

XML DSL
<message type="json">
  <validate>
    <json-path expression="$.user.name" value="Penny"/>
    <json-path expression="$['user']['name']" value="${userName}"/>
    <json-path expression="$.user.aliases" value="["penny","jenny","nanny"]"/>
    <json-path expression="$.user[?(@.admin)].password" value="@startsWith('$%00')@"/>
    <json-path expression="$.user.address[?(@.type='office')]"
        value="{"city":"Munich","street":"Company Street","type":"office"}"/>
  </validate>
</message>
Java DSL
receive(someEndpoint)
    .messageType(MessageType.JSON)
    .validate("$.user.name", "Penny")
    .validate("$['user']['name']", "${userName}")
    .validate("$.user.aliases", "["penny","jenny","nanny"]")
    .validate("$.user[?(@.admin)].password", "@startsWith('$%00')@")
    .validate("$.user.address[?(@.type='office')]", "{"city":"Munich","street":"Company Street","type":"office"}");

The above JSONPath expressions will be evaluated when Citrus validates the received message. The expression result is compared to the expected value where expectations can be static values as well as test variables and validation matcher expressions. In case a JSONPath expression should not be able to find any elements the test case will also fail.

JSON is a pretty simple yet powerful message format. Simply put, a JSON message just knows JSONObject, JSONArray and JSONValue items. The handling of JSONObject and JSONValue items in JSONPath expressions is straight forward. We just use a dot notated syntax for walking through the JSONObject hierarchy. The handling of JSONArray items is also not very difficult either. Citrus will try the best to convert JSONArray items to String representation values for comparison.

JSONPath expressions will only work on JSON message formats. This is why we have to tell Citrus the correct message format. By default Citrus is working with XML message data and therefore the XML validation mechanisms do apply by default. With the message type attribute set to json we make sure that Citrus enables JSON specific features on the message validation such as JSONPath support.

Now lets get a bit more complex with validation matchers and JSON object functions. Citrus tries to give you the most comfortable validation capabilities when comparing JSON object values and JSON arrays. One first thing you can use is object functions like keySet() or size() . This functionality is not covered by JSONPath out of the box but added by Citrus. See the following example on how to use it:

XML DSL
<message type="json">
  <validate>
    <json-path expression="$.user.keySet()" value="[id,name,admin,projects]"/>
    <json-path expression="$.user.aliases.size()" value="3"/>
  </validate>
</message>
Java DSL
receive(someEndpoint)
    .messageType(MessageType.JSON)
    .validate("$.user.keySet()", "[id,name,admin,projects]")
    .validate("$.user.aliases.size()", "3");

The object functions do return special JSON object related properties such as the set of keys for an object or the size of an JSON array.

Now lets get even more comfortable validation capabilities with matchers. Citrus supports Hamcrest matchers which gives us a very powerful way of validating JSON object elements and arrays. See the following examples that demonstrate how this works:

XML DSL
<message type="json">
  <validate>
    <json-path expression="$.user.keySet()" value="@assertThat(contains(id,name,admin,projects))@"/>
    <json-path expression="$.user.aliases.size()" value="@assertThat(allOf(greaterThan(0), lessThan(5)))@"/>
  </validate>
</message>
Java DSL
receive(someEndpoint)
    .messageType(MessageType.JSON)
    .validate("$.user.keySet()", contains("id","name","admin","projects"))
    .validate("$.user.aliases.size()", allOf(greaterThan(0), lessThan(5)));

When using the XML DSL we have to use the assertThat validation matcher syntax for defining the Hamcrest matchers. You can combine matcher implementation as seen in the allOf(greaterThan(0), lessThan(5)) expression. When using the Java DSL you can just add the matcher as expected result object. Citrus evaluates the matchers and makes sure everything is as expected. This is a very powerful validation mechanism as it combines the Hamcrest matcher capabilities with JSON message validation.

12.3. Extract variables with JSONPath

Citrus is able to save message content to test variables at test runtime. When an incoming message is passing the message validation the user can extract some values of that received message to new test variables for later use in the test. This is especially handsome when having to send back some dynamic values. So lets save some values using JSONPath:

<message type="json">
  <data>
    { "user":
      {
        "name": "Admin",
        "password": "secret",
        "admin": "true",
        "aliases": ["penny","chef","master"]
      }
    }
  </data>
  <extract>
    <message path="$.user.name" variable="userName"/>
    <message path="$.user.aliases" variable="userAliases"/>
    <message path="$.user[?(@.admin)].password" variable="adminPassword"/>
  </extract>
</message>

With this example we have extracted three new test variables via JSONPath expression evaluation. The three test variables will be available to all upcoming test actions. The variable values are:

userName=Admin
userAliases=["penny","chef","master"]
adminPassword=secret

As you can see we can also extract complex JSONObject items or JSONArray items. The test variable value is a String representation of the complex object.

12.4. Ignore with JSONPath

The next usage scenario for JSONPath expressions in Citrus is the ignoring of elements during message validation. As you already know Citrus provides powerful validation mechanisms for XML and JSON message format. The framework is able to compare received and expected message contents with powerful validator implementations. Now it this time we want to use a JSONPath expression for ignoring a very specific entry in the JSON object structure.

<message type="json">
  <data>
  {
      "users":
      [{
        "name": "Jane",
        "token": "?",
        "lastLogin": 0
      },
      {
        "name": "Penny",
        "token": "?",
        "lastLogin": 0
      },
      {
        "name": "Mary",
        "token": "?",
        "lastLogin": 0
      }]
  }
  </data>
  <ignore expression="$.users[*].token" />
  <ignore expression="$..lastLogin" />
</message>

This time we add JSONPath expressions as ignore statements. This means that we explicitly leave out the evaluated elements from validation. Obviously this mechanism is a good thing to do when dynamic message data simply is not deterministic such as timestamps and dynamic identifiers. In the example above we explicitly skip the token entry and all lastLogin values that are obviously timestamp values in milliseconds.

The JSONPath evaluation is very powerful when it comes to select a set of JSON objects and elements. This is how we can ignore several elements with one single JSONPath expression which is very powerful.

13. Test actions

This chapter gives a brief description to all test actions that a tester can incorporate into the test case. Besides sending and receiving messages the tester may access these actions in order to build a more complex test scenario that fits the desired use case.

13.1. Sending messages

In a integration test scenario we want to trigger processes and call interface services on the system under test. In order to do this we need to be able to send messages to various message transports. Therefore the send message test action in Citrus is one of the most important test actions. First of all let us have a look at the Citrus message definition in Citrus:

figure_001.jpg

A message consists of a message header (name-value pairs) and a message payload. Later in this section we will see different ways of constructing a message with payload and header values. But first of all let’s concentrate on a simple sending message action inside a test case.

XML DSL
<testcase name="SendMessageTest">
    <description>Basic send message example</description>

    <variables>
        <variable name="text" value="Hello Citrus!"/>
        <variable name="messageId" value="Mx1x123456789"/>
    </variables>

    <actions>
        <send endpoint="helloServiceEndpoint">
            <message name="helloMessage">
                <payload>
                    <TestMessage>
                        <Text>${text}</Text>
                    </TestMessage>
                </payload>
            </message>
            <header>
                <element name="Operation" value="sayHello"/>
                <element name="MessageId" value="${messageId}"/>
            </header>
        </send>
    </actions>
</testcase>

The message name is optional and defines the message identifier in the local message store. This message name is very useful when accessing the message content later on during the test case. The local message store is handled per test case and contains all exchanged messages. The sample uses both header and payload as message parts to send. In both parts you can use variable definitions (see ${text} and ${messageId}). So first of all let us recap what variables do. Test variables are defined at the very beginning of the test case and are valid throughout all actions that take place in the test. This means that actions can simply reference a variable by the expression ${variable-name} .

Use variables wherever you can! At least the important entities of a test should be defined as variables at the beginning. The test case improves maintainability and flexibility when using variables.

Now lets have a closer look at the sending action. The 'endpoint' attribute might catch your attention first. This attribute references a message endpoint in Citrus configuration by name. As previously mentioned the message endpoint definition lives in a separate configuration file and contains the actual message transport settings. In this example the "helloServiceEndpoint" is referenced which is a message endpoint for sending out messages via JMS or HTTP for instance.

The test case is not aware of any transport details, because it does not have to. The advantages are obvious: On the one hand multiple test cases can reference the message endpoint definition for better reuse. Secondly test cases are independent of message transport details. So connection factories, user credentials, endpoint uri values and so on are not present in the test case.

In other words the "endpoint" attribute of the <send> element specifies which message endpoint definition to use and therefore where the message should go to. Once again all available message endpoints are configured in a separate Citrus configuration file. We will come to this later on. Be sure to always pick the right message endpoint type in order to publish your message to the right destination.

If you do not like the XML language you can also use pure Java code to define the same test. In Java you would also make use of the message endpoint definition and reference this instance. The same test as shown above in Java DSL looks like this:

Java DSL designer
import org.testng.ITestContext;
import org.testng.annotations.Test;
import com.consol.citrus.annotations.CitrusTest;
import com.consol.citrus.dsl.testng.TestNGCitrusTestDesigner;

@Test
public class SendMessageTestDesigner extends TestNGCitrusTestDesigner {

    @CitrusTest(name = "SendMessageTest")
    public void sendMessageTest() {
        description("Basic send message example");

        variable("text", "Hello Citrus!");
        variable("messageId", "Mx1x123456789");

        send("helloServiceEndpoint")
                .name("helloMessage")
                .payload("<TestMessage>" +
                    "<Text>${text}</Text>" +
                    "</TestMessage>")
                .header("Operation", "sayHello")
                .header("RequestTag", "${messageId}");
    }
}
Java DSL runner
import org.testng.ITestContext;
import org.testng.annotations.Test;
import com.consol.citrus.annotations.CitrusTest;
import com.consol.citrus.dsl.testng.TestNGCitrusTestRunner;

@Test
public class SendMessageTestRunner extends TestNGCitrusTestRunner {

    @CitrusTest(name = "SendMessageTest")
    public void sendMessageTest() {
        variable("text", "Hello Citrus!");
        variable("messageId", "Mx1x123456789");

        send(action -> action.endpoint("helloServiceEndpoint")
                .name("helloMessage")
                .payload("<TestMessage>" +
                        "<Text>${text}</Text>" +
                    "</TestMessage>")
                .header("Operation", "sayHello")
                .header("RequestTag", "${messageId}"));
    }
}

Instead of using the XML tags for send we use methods from TestNGCitrusTestDesigner class. The same message endpoint is referenced within the send message action.

Now that the message sender pattern is clear we can concentrate on how to specify the message content to be sent. There are several possibilities for you to define message content in Citrus:

message

This element constructs the message to be sent. There are several child elements available:

payload

Nested XML payload as direct child node.

data

Inline CDATA definition of the message payload

resource

External file resource holding the message payload The syntax would be: <resource file="classpath:com/consol/citrus/messages/TestRequest.xml" /> The file path prefix indicates the resource type, so the file location is resolved either as file system resource (file:) or classpath resource (classpath:).

element

Explicitly overwrite values in the XML message payload using XPath. You can replace message content with dynamic values before sending. Each <element> entry provides a "path" and "value" attribute. The "path" gives a XPath expression evaluating to a XML node element or attribute in the message. The "value" can be a variable expression or any other static value. Citrus will replace the value before sending the message.

header

Defines a header for the message (e.g. JMS header information or SOAP header):

element

Each header receives a "name" and "value". The "name" will be the name of the header entry and "value" its respective value. Again the usage of variable expressions as value is supported here, too.

XML DSL
<send endpoint="helloServiceEndpoint">
    <message>
        <payload>
            <!-- message payload as XML -->
        </payload>
    </message>
</send>
<send endpoint="helloServiceEndpoint">
    <message>
        <data>
            <![CDATA[
                <!-- message payload as XML -->
            ]]>
        </data>
    </message>
</send>
<send endpoint="helloServiceEndpoint">
    <message>
        <resource file="classpath:com/consol/citrus/messages/TestRequest.xml" />
    </message>
</send>

The most important thing when dealing with sending actions is to prepare the message payload and header. You are able to construct the message payload either by nested XML child nodes (payload), as inline CDATA (<data>) or external file (<resource>).

Sometimes the nested XML message payload elements may cause XSD schema validation rule violations. This is because of variable values not fitting the XSD schema rules for example. In this scenario you could also use simple CDATA sections as payload data. In this case you need to use the `<data>` element in contrast to the `<payload>` element that we have used in our examples so far.

With this alternative you can skip the XML schema validation from your IDE at design time. Unfortunately you will loose the XSD auto completion features many XML editors offer when constructing your payload.

The The same possibilities apply to the Citrus Java DSL.

Java DSL designer
@CitrusTest
public void messagingTest() {
    send("helloServiceEndpoint")
        .payload("<TestMessage>" +
                "<Text>Hello!</Text>" +
            "</TestMessage>");
}
@CitrusTest
public void messagingTest() {
    send("helloServiceEndpoint")
        .payload(new ClassPathResource("com/consol/citrus/messages/TestRequest.xml"));
}
@CitrusTest
public void messagingTest() {
    send("helloServiceEndpoint")
        .payloadModel(new TestRequest("Hello Citrus!"));
}
@CitrusTest
public void messagingTest() {
    send("helloServiceEndpoint")
        .message(new DefaultMessage("Hello World!")));
}

Besides defining message payloads as normal Strings and via external file resource (classpath and file system) you can also use model objects as payload data in Java DSL. This model object payload requires a proper message marshaller that should be available as Spring bean inside the application context. By default Citrus is searching for a bean of type org.springframework.oxm.Marshaller .

In case you have multiple message marshallers in the application context you have to tell Citrus which one to use in this particular send message action.

@CitrusTest
public void messagingTest() {
    send("helloServiceEndpoint")
        .payloadModel(new TestRequest("Hello Citrus!"), "myMessageMarshallerBean");
}

Now Citrus will marshal the message payload with the message marshaller bean named myMessageMarshallerBean . This way you can have multiple message marshaller implementations active in your project (XML, JSON, and so on).

Last not least the message can be defined as Citrus message object. Here you can choose one of the different message implementations used in Citrus for SOAP, Http or JMS messages. Or you just use the default message implementation or maybe a custom implementation.

Before sending takes place you can explicitly overwrite some message values in payload. You can think of overwriting specific message elements with variable values. Also you can overwrite values using XPath (xpath) or JSONPath (json-path) expressions.

The message header is part of our duty of defining proper messages, too. So Citrus uses name-value pairs like "Operation" and "MessageId" in the next example to set message header entries. Depending on what message endpoint is used and which message transport underneath the header values will be shipped in different ways. In JMS the headers go to the header section of the message, in Http we set mime headers accordingly, in SOAP we can access the SOAP header elements and so on. Citrus aims to do the hard work for you. So Citrus knows how to set headers on different message transports.

XML DSL
<send endpoint="helloServiceEndpoint">
    <message>
        <payload>
            <TestMessage>
                <Text>Hello!</Text>
            </TestMessage>
        </payload>
    </message>
    <header>
        <element name="Operation" value="sayHello"/>
    </header>
</receive>

The message headers to send are defined by a simple name and value pair. Of course you can use test variables in header values as well. Let’s see how this looks like in Java DSL:

Java DSL designer
@CitrusTest
public void messagingTest() {
    receive("helloServiceEndpoint")
        .payload("<TestMessage>" +
                "<Text>Hello!</Text>" +
            "</TestMessage>")
        .header("Operation", "sayHello");
}
Java DSL runner
@CitrusTest
public void messagingTest() {
    receive(action -> action.endpoint("helloServiceEndpoint")
        .payload("<TestMessage>" +
                "<Text>Hello!</Text>" +
            "</TestMessage>")
        .header("Operation", "sayHello"));
}

This is basically how to send messages in Citrus. The test case is responsible for constructing the message content while the predefined message endpoint holds transport specific settings. Test cases reference endpoint components to publish messages to the outside world. The variable support in message payload and message header enables you to add dynamic values before sending out the message.

13.2. Receive messages

Just like sending messages the receiving part is a very important action in an integration test. Honestly the receive action is even more important in Citrus as we also want to validate the incoming message contents. We are writing a test so we also need assertions and checks that everything works as expected.

As already mentioned before a message consists of a message header (name-value pairs) and a message payload. Later in this document we will see how to validate incoming messages with payload and header values. We start with a very simple example:

XML DSL
<receive endpoint="helloServiceEndpoint">
    <message name="helloRequest">
        <payload>
            <TestMessage>
                <Text>${text}</Text>
            </TestMessage>
        </payload>
    </message>
    <header>
        <element name="Operation" value="sayHello"/>
        <element name="MessageId" value="${messageId}"/>
    </header>
</receive>

Overall the receive message action looks quite similar to the send message action. Concepts are identical as we define the message content with payload and header values. The message name is optional and defines the message identifier in the local message store. This message name is very useful when accessing the message content later on during the test case. The local message store is handled per test case and contains all exchanged messages.

We can use test variables in both message payload an headers. Now let us have a look at the Java DSL representation of this simple example:

Java DSL designer
@CitrusTest
public void messagingTest() {
    receive("helloServiceEndpoint")
        .name("helloRequest")
        .payload("<TestMessage>" +
                    "<Text>${text}</Text>" +
                "</TestMessage>")
        .header("Operation", "sayHello")
        .header("MessageId", "${messageId}");
}
Java DSL runner
@CitrusTest
public void messagingTest() {
    receive(action -> action.endpoint("helloServiceEndpoint")
                .name("helloRequest")
                .payload("<TestMessage>" +
                      "<Text>${text}</Text>" +
                    "</TestMessage>")
                .header("Operation", "sayHello")
                .header("MessageId", "${messageId}"));
}

The receive action waits for a message to arrive. The whole test execution is stopped while waiting for the message. This is important to ensure the step by step test workflow processing. Of course you can specify message timeouts so the receiver will only wait a given amount of time before raising a timeout error. Following from that timeout exception the test case fails as the message did not arrive in time. Citrus defines default timeout settings for all message receiving tasks.

In a good case scenario the message arrives in time and the content can be validated as a next step. This validation can be done in various ways. On the one hand you can specify a whole XML message that you expect as control template. In this case the received message structure is compared to the expected message content element by element. On the other hand you can use explicit element validation where only a small subset of message elements is included into validation.

Besides the message payload Citrus will also perform validation on the received message header values. Test variable usage is supported as usual during the whole validation process for payload and header checks.

In general the validation component (validator) in Citrus works hand in hand with a message receiving component as the following figure shows:

figure_005.jpg

The message receiving component passes the message to the validator where the individual validation steps are performed. Let us have a closer look at the validation options and features step by step.

13.2.1. Validate message payloads

The most detailed validation of incoming messages is to define some expected message payload. The Citrus message validator will then perform a detailed message payload comparison. The incoming message has to match exactly to the expected message payload. The different message validator implementations in Citrus provide deep comparison of message structures such as XML, JSON and so on.

So by defining an expected message payload we validate the incoming message in syntax and semantics. In case a difference is identified by the message validator the validation and the test case fails with respective exceptions. This is how you can define message payloads in receive action:

XML DSL
<receive endpoint="helloServiceEndpoint">
<message>
  <payload>
    <!-- message payload as XML -->
  </payload>
</message>
</receive>
<receive endpoint="helloServiceEndpoint">
<message>
  <data>
      <![CDATA[
        <!-- message payload as XML -->
      ]]>
  </data>
</message>
</receive>
<receive endpoint="helloServiceEndpoint">
<message>
  <resource file="classpath:com/consol/citrus/messages/TestRequest.xml" />
</message>
</receive>

The three examples above represent three different ways of defining the message payload in a receive message action. On the one hand we can use inline message payloads as nested XML or CDATA sections in the test. On the other hand we can load the message content from external file resource.

Sometimes the nested XML message payload elements may cause XSD schema validation rule violations. This is because of variable values not fitting the XSD schema rules for example. In this scenario you could also use simple CDATA sections as payload data. In this case you need to use the `<data>` element in contrast to the `<payload>` element that we have used in our examples so far.

With this alternative you can skip the XML schema validation from your IDE at design time. Unfortunately you will loose the XSD auto completion features many XML editors offer when constructing your payload.

In Java DSL we also have multiple options for specifying the message payloads:

Java DSL designer
@CitrusTest
public void messagingTest() {
    receive("helloServiceEndpoint")
        .payload("<TestMessage>" +
                    "<Text>Hello!</Text>" +
                "</TestMessage>");
}
@CitrusTest
public void messagingTest() {
    receive("helloServiceEndpoint")
        .payload(new ClassPathResource("com/consol/citrus/messages/TestRequest.xml"));
}
@CitrusTest
public void messagingTest() {
    receive("helloServiceEndpoint")
        .payloadModel(new TestRequest("Hello Citrus!"));
}
@CitrusTest
public void messagingTest() {
    receive("helloServiceEndpoint")
        .message(new DefaultMessage("Hello World!")));
}

The examples above represent the basic variations of how to define message payloads in Citrus Java DSL. The payload can be a simple String or a Spring file resource (classpath or file system). In addition to that we can use a model object. When using model objects as payloads we need a proper message marshaller implementation in the Spring application context. By default this is a marshaller bean of type org.springframework.oxm.Marshaller that has to be present in the Spring application context. You can add such a bean for XML and JSON message marshalling for instance.

In case you have multiple message marshallers in the application context you have to tell Citrus which one to use in this particular send message action.

@CitrusTest
public void messagingTest() {
    receive("helloServiceEndpoint")
        .payloadModel(new TestRequest("Hello Citrus!"), "myMessageMarshallerBean");
}

Now Citrus will marshal the message payload with the message marshaller bean named myMessageMarshallerBean . This way you can have multiple message marshaller implementations active in your project (XML, JSON, and so on).

Last not least the message can be defined as Citrus message object. Here you can choose one of the different message implementations used in Citrus for SOAP, Http or JMS messages. Or you just use the default message implementation or maybe a custom implementation.

In general the expected message content can be manipulated using XPath (xpath) or JSONPath (json-path). In addition to that you can ignore some elements that are skipped in comparison. We will describe this later on in this section. Now lets continue with message header validation.

13.2.2. Validate message headers

Message headers are used widely in enterprise messaging solution: The message headers are part of the message semantics and need to be validated, too. Citrus can validate message header by name and value.

XML DSL
<receive endpoint="helloServiceEndpoint">
    <message>
        <payload>
        <TestMessage>
            <Text>Hello!</Text>
        </TestMessage>
        </payload>
    </message>
    <header>
        <element name="Operation" value="sayHello"/>
    </header>
</receive>

The expected message headers are defined by a name and value pair. Citrus will check that the expected message header is present and will check the value. In case the message header is not found or the value does not match Citrus will raise an exception and the test fails. You can use validation matchers (validation-matcher) for a more powerful validation of header values, too.

Let’s see how this looks like in Java DSL:

Java DSL designer
@CitrusTest
public void messagingTest() {
    receive("helloServiceEndpoint")
        .payload("<TestMessage>" +
                "<Text>Hello!</Text>" +
            "</TestMessage>")
        .header("Operation", "sayHello");
}
Java DSL runner
@CitrusTest
public void messagingTest() {
    receive(action -> action.endpoint("helloServiceEndpoint")
                .payload("<TestMessage>" +
                      "<Text>Hello!</Text>" +
                    "</TestMessage>")
                .header("Operation", "sayHello"));
}

Header definition in Java DSL is straight forward as we just define name and value as usual. This completes the message validation when receiving a message in Citrus. The message validator implementations may add additional validation capabilities such as XML schema validation or XPath and JSONPath validation. Please refer to the respective chapters in this guide to learn more about that.

13.2.3. Message selectors

The <selector> element inside the receiving action defines key-value pairs in order to filter the messages being received. The filter applies to the message headers. This means that a receiver will only accept messages matching a header element value. In messaging applications the header information often holds message ids, correlation ids, operation names and so on. With this information given you can explicitly listen for messages that belong to your test case. This is very helpful to avoid receiving messages that are still available on the message destination.

Lets say the tested software application keeps sending messages that belong to previous test cases. This could happen in retry situations where the application error handling automatically tries to solve a communication problem that occurred during previous test cases. As a result a message destination (e.g. a JMS message queue) contains messages that are not valid any more for the currently running test case. The test case might fail because the received message does not apply to the actual use case. So we will definitely run into validation errors as the expected message control values do not match.

Now we have to find a way to avoid these problems. The test could filter the messages on a destination to only receive messages that apply for the use case that is being tested. The Java Messaging System (JMS) came up with a message header selector that will only accept messages that fit the expected header values.

Let us have a closer look at a message selector inside a receiving action:

XML DSL
<selector>
    <element name="correlationId" value="Cx1x123456789"/>
    <element name="operation" value="getOrders"/>
</selector>
Java DSL designer
@CitrusTest
public void receiveMessageTest() {
    receive("testServiceEndpoint")
        .selector("correlationId='Cx1x123456789' AND operation='getOrders'");
}
Java DSL runner
@CitrusTest
public void receiveMessageTest() {
    receive(action -> action.endpoint("testServiceEndpoint")
                .selector("correlationId='Cx1x123456789' AND operation='getOrders'"));
}

This example shows how message selectors work. The selector will only accept messages that meet the correlation id and the operation in the header values. All other messages on the message destination are ignored. The selector elements are automatically associated to each other using the logical AND operator. This means that the message selector string would look like this: correlationId = 'Cx1x123456789' AND operation = 'getOrders' .

Instead of using several elements in the selector you can also define a selector string directly which gives you more power in constructing the selection logic yourself. This way you can use AND logical operators yourself.

<selector>
    <value>
        correlationId = 'Cx1x123456789' AND operation = 'getOrders'
    </value>
</selector>
In case you want to run tests in parallel message selectors become essential in your test cases. The different tests running at the same time will steal messages from each other when you lack of message selection mechanisms.
Previously only JMS message destinations offered support for message selectors! With Citrus version 1.2 we introduced message selector support for Spring Integration message channels, too (see message-channel-selector-support).

13.2.4. Groovy MarkupBuilder

With the Groovy MarkupBuilder you can build XML message payloads in a simple way, without having to write the typical XML overhead. For example we use a Groovy script to construct the XML message to be sent out. Instead of a plain CDATA XML section or the nested payload XML data we write a Groovy script snippet. The Groovy MarkupBuilder generates the XML message payload with exactly the same result:

XML DSL
<send endpoint="helloServiceEndpoint">
<message>
    <builder type="groovy">
        markupBuilder.TestMessage {
                MessageId('${messageId}')
                Timestamp('?')
                VersionId('2')
                Text('Hello Citrus!')
            }
        }
    </builder>
    <element path="/TestMessage/Timestamp"
                value="${createDate}"/>
    </message>
    <header>
        <element name="Operation" value="sayHello"/>
        <element name="MessageId" value="${messageId}"/>
    </header>
</send>

We use the builder element with type groovy and the MarkupBuilder code is directly written to this element. As you can see from the example above, you can mix XPath and Groovy markup builder code. The MarkupBuilder syntax is very easy and follows the simple rule: markupBuilder.ROOT-ELEMENT{ CHILD-ELEMENTS } . However the tester has to follow some simple rules and naming conventions when using the Citrus MarkupBuilder extension:

  • The MarkupBuilder is accessed within the script over an object named markupBuilder. The name of the custom root element follows with all its child elements.

  • Child elements may be defined within curly brackets after the root-element (the same applies for further nested child elements)

  • Attributes and element values are defined within round brackets, after the element name

  • Attribute and element values have to stand within apostrophes (e.g. attribute-name: 'attribute-value')

The Groovy MarkupBuilder script may also be used within receive actions as shown in the following listing:

XML DSL
<send endpoint="helloServiceEndpoint">
    <message>
        <builder type="groovy" file="classpath:com/consol/citrus/groovy/helloRequest.groovy"/>
    </message>
</send>

<receive endpoint="helloServiceEndpoint" timeout="5000">
    <message>
        <builder type="groovy">
            markupBuilder.TestResponse(xmlns: 'http://www.consol.de/schemas/samples/sayHello.xsd'){
                MessageId('${messageId}')
                CorrelationId('${correlationId}')
                User('HelloService')
                Text('Hello ${user}')
            }
        </builder>
    </message>
</receive>

As you can see it is also possible to define the script as external file resource. In addition to that namespace support is given as normal attribute definition within the round brackets after the element name.

The MarkupBuilder implementation in Groovy offers great possibilities in defining message payloads. We do not need to write XML tag overhead and we can construct complex message payloads with Groovy logic like iterations and conditional elements. For detailed MarkupBuilder descriptions please see the official Groovy documentation.

13.3. Database actions

In many cases it is necessary to access the database during a test. This enables a tester to also validate the persistent data in a database. It might also be helpful to prepare the database with some test data before running a test. You can do this using the two database actions that are described in the following sections.

In general Citrus handles SELECT statements differently to other statements like INSERT, UPDATE and DELETE. When executing a SQL query with SELECT you are able to add validation steps on the result sets returned from the database. This is not allowed when executing update statements like INSERT, UPDATE, DELETE.

Do not mix statements of type SELECT with others in a single sql test action. This will lead to errors because validation steps are not valid for statements other than SELECT. Please use separate test actions for update statements.

13.3.1. SQL update, insert, delete

The <sql> action simply executes a group of SQL statements in order to change data in a database. Typically the action is used to prepare the database at the beginning of a test or to clean up the database at the end of a test. You can specify SQL statements like INSERT, UPDATE, DELETE, CREATE TABLE, ALTER TABLE and many more.

On the one hand you can specify the statements as inline SQL or stored in an external SQL resource file as shown in the next two examples.

XML DSL
<actions>
    <sql datasource="someDataSource">
        <statement>DELETE FROM CUSTOMERS</statement>
        <statement>DELETE FROM ORDERS</statement>
    </sql>

    <sql datasource="myDataSource">
        <resource file="file:tests/unit/resources/script.sql"/>
    </sql>
</actions>
Java DSL designer
@Autowired
@Qualifier("myDataSource")
private DataSource dataSource;

@CitrusTest
public void sqlTest() {
    sql(dataSource)
        .statement("DELETE FROM CUSTOMERS")
        .statement("DELETE FROM ORDERS");

    sql(dataSource)
        .sqlResource("file:tests/unit/resources/script.sql");
}
Java DSL runner
@Autowired
@Qualifier("myDataSource")
private DataSource dataSource;

@CitrusTest
public void sqlTest() {
    sql(action -> action.dataSource(dataSource)
        .statement("DELETE FROM CUSTOMERS")
        .statement("DELETE FROM ORDERS"));

    sql(action -> action.dataSource(dataSource)
        .sqlResource("file:tests/unit/resources/script.sql"));
}

The first action uses inline SQL statements defined directly inside the test case. The next action uses an external SQL resource file instead. The file resource can hold several SQL statements separated by new lines. All statements inside the file are executed sequentially by the framework.

You have to pay attention to some rules when dealing with external SQL resources.
  • Each statement should begin in a new line

  • It is not allowed to define statements with word wrapping

  • Comments begin with two dashes "–"

The external file is referenced either as file system resource or class path resource, by using the "file:" or "classpath:" prefix.

Both examples use the "datasource" attribute. This value defines the database data source to be used. The connection to a data source is mandatory, because the test case does not know about user credentials or database names. The 'datasource' attribute references predefined data sources that are located in a separate Spring configuration file.

13.3.2. SQL query

The <sql> query action is specially designed to execute SQL queries (SELECT * FROM). So the test is able to read data from a database. The query results are validated against expected data as shown in the next example.

XML DSL
<sql datasource="testDataSource">
    <statement>select NAME from CUSTOMERS where ID='${customerId}'</statement>
    <statement>select count(*) from ERRORS</statement>
    <statement>select ID from ORDERS where DESC LIKE 'Def%'</statement>
    <statement>select DESCRIPTION from ORDERS where ID='${id}'</statement>

    <validate column="ID" value="1"/>
    <validate column="NAME" value="Christoph"/>
    <validate column="COUNT(*)" value="${rowsCount}"/>
    <validate column="DESCRIPTION" value="null"/>
</sql>
Java DSL designer
@Autowired
@Qualifier("testDataSource")
private DataSource dataSource;

@CitrusTest
public void databaseQueryTest() {
    query(dataSource)
          .statement("select NAME from CUSTOMERS where CUSTOMER_ID='${customerId}'")
          .statement("select COUNT(1) as overall_cnt from ERRORS")
          .statement("select ORDER_ID from ORDERS where DESCRIPTION LIKE 'Migrate%'")
          .statement("select DESCRIPTION from ORDERS where ORDER_ID = 2")
          .validate("ORDER_ID", "1")
          .validate("NAME", "Christoph")
          .validate("OVERALL_CNT", "${rowsCount}")
          .validate("DESCRIPTION", "NULL");
}
Java DSL runner
@Autowired
@Qualifier("testDataSource")
private DataSource dataSource;

@CitrusTest
public void databaseQueryTest() {
    query(action -> action.dataSource(dataSource)
            .statement("select NAME from CUSTOMERS where CUSTOMER_ID='${customerId}'")
            .statement("select COUNT(1) as overall_cnt from ERRORS")
            .statement("select ORDER_ID from ORDERS where DESCRIPTION LIKE 'Migrate%'")
            .statement("select DESCRIPTION from ORDERS where ORDER_ID = 2")
            .validate("ORDER_ID", "1")
            .validate("NAME", "Christoph")
            .validate("OVERALL_CNT", "${rowsCount}")
            .validate("DESCRIPTION", "NULL"));
}

The action offers a wide range of validating functionality for database result sets. First of all you have to select the data via SQL statements. Here again you have the choice to use inline SQL statements or external file resource pattern.

The result sets are validated through <validate> elements. It is possible to do a detailed check on every selected column of the result set. Simply refer to the selected column name in order to validate its value. The usage of test variables is supported as well as database expressions like count(), avg(), min(), max().

You simply define the <validate> entry with the column name as the "column" attribute and any expected value expression as expected "value". The framework then will check the column to fit the expected value and raise validation errors in case of mismatch.

Looking at the first SELECT statement in the example you will see that test variables are supported in the SQL statements. The framework will replace the variable with its respective value before sending it to the database.

In the validation section variables can be used too. Look at the third validation entry, where the variable "${rowsCount}" is used. The last validation in this example shows, that NULL values are also supported as expected values.

If a single validation happens to fail, the whole action will fail with respective validation errors.

The validation with "<validate column="…​" value="…​"/>" meets single row result sets as you specify a single column control value. In case you have multiple rows in a result set you rather need to validate the columns with multiple control values like this:
  <validate column="someColumnName">
      <values>
          <value>Value in 1st row</value>
          <value>Value in 2nd row</value>
          <value>Value in 3rd row</value>
          <value>Value in x row</value>
      </values>
  </validate>

Within Java you can pass a variable argument list to the validate method like this:

query(dataSource)
    .statement("select NAME from WEEKDAYS where NAME LIKE 'S%'")
    .validate("NAME", "Saturday", "Sunday")

Next example shows how to work with multiple row result sets and multiple values to expect within one column:

  <sql datasource="testDataSource">
      <statement>select WEEKDAY as DAY, DESCRIPTION from WEEK</statement>
      <validate column="DAY">
          <values>
              <value>Monday</value>
              <value>Tuesday</value>
              <value>Wednesday</value>
              <value>Thursday</value>
              <value>Friday</value>
              <value>@ignore@</value>
              <value>@ignore@</value>
          </values>
      </validate>
      <validate column="DESCRIPTION">
          <values>
              <value>I hate Mondays!</value>
              <value>Tuesday is sports day</value>
              <value>The mid of the week</value>
              <value>Thursday we play chess</value>
              <value>Friday, the weekend is near!</value>
              <value>@ignore@</value>
              <value>@ignore@</value>
          </values>
      </validate>
  </sql>

For the validation of multiple rows the `<validate>` element is able to host a list of control values for a column. As you can see from the example above, you have to add a control value for each row in the result set. This also means that we have to take care of the total number of rows. Fortunately we can use the ignore placeholder, in order to skip the validation of a specific row in the result set. Functions and variables are supported as usual.

It is important, that the control values are defined in the correct order, because they are compared one on one with the actual result set coming from database query. You may need to add "order by" SQL expressions to get the right order of rows returned. If any of the values fails in validation or the total number of rows is not equal, the whole action will fail with respective validation errors.

13.3.3. Transaction management

By default no transactions are used when Citrus executes SQL statements on a datasource. You can enable transaction management by selecting a transaction manager.

XML DSL
<actions>
    <sql datasource="someDataSource"
         transaction-manager="someTransactionManager"
         transaction-timeout="15000"
         transaction-isolation-level="ISOLATION_READ_COMMITTED">
        <statement>DELETE FROM CUSTOMERS</statement>
        <statement>DELETE FROM ORDERS</statement>
    </sql>
</actions>
Java DSL
@Autowired
@Qualifier("myDataSource")
private DataSource dataSource;

@CitrusTest
public void sqlTest() {
    sql(dataSource)
        .transactionManager(transactionManager)
        .transactionTimeout(15000)
        .transactionIsolationLevel("ISOLATION_READ_COMMITTED")
        .statement("DELETE FROM CUSTOMERS")
        .statement("DELETE FROM ORDERS");
}

The transaction-manager attribute references a Spring bean of type "org.springframework.transaction.PlatformTransactionManager". You can add this transaction manager to the Spring bean configuration:

<bean id="someTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <constructor-arg ref="someDataSource"/>
</bean>

The transaction isolation level as well as the transaction timeout get set on the transaction definition used during SQL statement execution. The isolation level should evaluate to one of the constants given in "org.springframework.transaction.TransactionDefinition". Valid isolation level are:

  • ISOLATION_DEFAULT

  • ISOLATION_READ_UNCOMMITTED

  • ISOLATION_READ_COMMITTED

  • ISOLATION_REPEATABLE_READ

  • ISOLATION_SERIALIZABLE

13.3.4. Groovy SQL result set validation

Groovy provides great support for accessing Java list objects and maps. As a Java SQL result set is nothing but a list of map representations, where each entry in the list defines a row in the result set and each map entry represents the columns and values. So with Groovy’s list and map access we have great possibilities to validate a SQL result set - out of the box.

XML DSL
<sql datasource="testDataSource">
    <statement>select ID from CUSTOMERS where NAME='${customerName}'</statement>
    <statement>select ORDERTYPE, STATUS from ORDERS where ID='${orderId}'</statement>

    <validate-script type="groovy">
        assert rows.size() == 2
        assert rows[0].ID == '1'
        assert rows[1].STATUS == 'in progress'
        assert rows[1] == [ORDERTYPE:'SampleOrder', STATUS:'in progress']
    </validate-script>
</sql>
Java DSL designer
query(dataSource)
    .statement("select ORDERTYPE, STATUS from ORDERS where ID='${orderId}'")
    .validateScript("assert rows.size == 2;" +
            "assert rows[0].ID == '1';" +
            "assert rows[0].STATUS == 'in progress';", "groovy");
Java DSL runner
query(action -> action.dataSource(dataSource)
    .statement("select ORDERTYPE, STATUS from ORDERS where ID='${orderId}'")
    .validateScript("assert rows.size == 2;" +
            "assert rows[0].ID == '1';" +
            "assert rows[0].STATUS == 'in progress';", "groovy"));

As you can see Groovy provides fantastic access methods to the SQL result set. We can browse the result set with named column values and check the size of the result set. We are also able to search for an entry, iterate over the result set and have other helpful operations. For a detailed description of the list and map handling in Groovy my advice for you is to have a look at the official Groovy documentation.

In general other script languages do also support this kind of list and map access. For now we just have implemented the Groovy script support, but the framework is ready to work with all other great script languages out there, too (e.g. Scala, Clojure, Fantom, etc.). So if you prefer to work with another language join and help us implement those features.

13.3.5. Save result set values

Now the validation of database entries is a very powerful feature but sometimes we simply do not know the persisted content values. The test may want to read database entries into test variables without validation. Citrus is able to do that with the following <extract> expressions:

XML DSL
<sql datasource="testDataSource">
    <statement>select ID from CUSTOMERS where NAME='${customerName}'</statement>
    <statement>select STATUS from ORDERS where ID='${orderId}'</statement>

    <extract column="ID" variable="${customerId}"/>
    <extract column="STATUS" variable="${orderStatus}"/>
</sql>
Java DSL designer
query(dataSource)
    .statement("select STATUS from ORDERS where ID='${orderId}'")
    .extract("STATUS", "orderStatus");
Java DSL runner
query(action -> action.dataSource(dataSource)
    .statement("select STATUS from ORDERS where ID='${orderId}'")
    .extract("STATUS", "orderStatus"));

We can save the database column values directly to test variables. Of course you can combine the value extraction with the normal column validation described earlier in this chapter. Please keep in mind that we can not use these operations on result sets with multiple rows. Citrus will always use the first row in a result set.

13.4. Sleep

This action shows how to make the test framework sleep for a given amount of time. The attribute 'time' defines the amount of time to wait in seconds. As shown in the next example decimal values are supported too. When no waiting time is specified the default time of 50000 milliseconds applies.

XML DSL
<testcase name="sleepTest">
    <actions>
        <sleep seconds="3.5"/>

        <sleep milliseconds="500"/>

        <sleep/>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void sleepTest() {
    sleep(500); // sleep 500 milliseconds

    sleep(); // sleep default time
}

When should somebody use this action? To us this action was always very useful in case the test needed to wait until an application had done some work. For example in some cases the application took some time to write some data into the database. We waited then a small amount of time in order to avoid unnecessary test failures, because the test framework simply validated the database too early. Or as another example the test may wait a given time until retry mechanisms are triggered in the tested application and then proceed with the test actions.

13.5. Java

The test framework is written in Java and runs inside a Java virtual machine. The functionality of calling other Java objects and methods in this same Java VM through Java Reflection is self-evident. With this action you can call any Java API available at runtime through the specified Java classpath.

The action syntax looks like follows:

<java class="com.consol.citrus.test.util.InvocationDummy">
    <constructor>
        <argument type="">Test Invocation</argument>
    </constructor>
    <method name="invoke">
        <argument type="String[]">1,2</argument>
    </method>
</java>

<java class="com.consol.citrus.test.util.InvocationDummy">
    <constructor>
        <argument type="">Test Invocation</argument>
    </constructor>
    <method name="invoke">
        <argument type="int">4</argument>
        <argument type="String">Test Invocation</argument>
        <argument type="boolean">true</argument>
    </method>
</java>

<java class="com.consol.citrus.test.util.InvocationDummy">
    <method name="main">
        <argument type="String[]">4,Test,true </argument>
    </method>
</java>

The Java class is specified by fully qualified class name. Constructor arguments are added using the <constructor> element with a list of <argument> child elements. The type of the argument is defined within the respective attribute "type". By default the type would be String.

The invoked method on the Java object is simply referenced by its name. Method arguments do not bring anything new after knowing the constructor argument definition, do they?.

Method arguments support data type conversion too, even string arrays (useful when calling CLIs). In the third action in the example code you can see that colon separated strings are automatically converted to string arrays.

Simple data types are defined by their name (int, boolean, float etc.). Be sure that the invoked method and class constructor fit your arguments and vice versa, otherwise you will cause errors at runtime.

Besides instantiating a fully new object instance for a class how about reusing a bean instance available in Spring bean container. Simply use the ref attribute and refer to an existing bean in Spring application context.

<java ref="invocationDummy">
    <method name="invoke">
        <argument type="int">4</argument>
        <argument type="String">Test Invocation</argument>
        <argument type="boolean">true</argument>
    </method>
</java>

<bean id="invocationDummy" class="com.consol.citrus.test.util.InvocationDummy"/>

The method is invoked on the Spring bean instance. This is very useful as you can inject other objects (e.g. via Autowiring) to the Spring bean instance before method invocation in test takes place. This enables you to execute any Java logic inside a test case.

13.6. Receive timeout

In some cases it might be necessary to validate that a message is not present on a destination. This means that this action expects a timeout when receiving a message from an endpoint destination. For instance the tester intends to ensure that no message is sent to a certain destination in a time period. In that case the timeout would not be a test aborting error but the expected behavior. And in contrast to the normal behavior when a message is received in the time period the test will fail with error.

In order to validate such a timeout situation the action <expectTimout> shall help. The usage is very simple as the following example shows:

XML DSL
<testcase name="receiveJMSTimeoutTest">
    <actions>
        <expect-timeout endpoint="myEndpoint" wait="500"/>
    </actions>
</testcase>
Java DSL designer
@Autowired
@Qualifier("myEndpoint")
private Endpoint myEndpoint;

@CitrusTest
public void receiveTimeoutTest() {
    receiveTimeout(myEndpoint)
        .timeout(500);
}
Java DSL runner
@Autowired
@Qualifier("myEndpoint")
private Endpoint myEndpoint;

@CitrusTest
public void receiveTimeoutTest() {
    receiveTimeout(action -> action.endpoint(myEndpoint)
                    .timeout(500));
}

The action offers two attributes:

endpoint

Reference to a message endpoint that will try to receive messages.

wait/timeout

Time period to wait for messages to arrive

Sometimes you may want to add some selector on the timeout receiving action. This way you can very selective check on a message to not be present on a message destination. This is possible with defining a message selector on the test action as follows.

XML DSL
<expect-timeout endpoint="myEndpoint" wait="500">
  <select>MessageId='123456789'<select/>
<expect-timeout/>
Java DSL designer
@CitrusTest
public void receiveTimeoutTest() {
    receiveTimeout(myEndpoint)
        .selector("MessageId = '123456789'")
        .timeout(500);
}
Java DSL runner
@CitrusTest
public void receiveTimeoutTest() {
    receiveTimeout(action -> action.endpoint(myEndpoint)
                    .selector("MessageId = '123456789'")
                    .timeout(500));
}

13.7. Echo

The <echo> action prints messages to the console/logger. This functionality is useful when debugging test runs. The property "message" defines the text that is printed. Tester might use it to print out debug messages and variables as shown the next code example:

XML DSL
<testcase name="echoTest">
    <variables>
        <variable name="date" value="citrus:currentDate()"/>
    </variables>
    <actions>
        <echo>
            <message>Hello Test Framework</message>
        </echo>

        <echo>
            <message>Current date is: ${date}</message>
        </echo>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void echoTest() {
    variable("date", "citrus:currentDate()");

    echo("Hello Test Framework");
    echo("Current date is: ${date}");
}

Result on the console:

Hello Test Framework
Current time is: 05.08.2008

13.8. Stop time

Time measurement during a test can be very helpful. The <trace-time> action creates and monitors multiple time lines. The action offers the attribute id to identify a time line. The tester can of course use more than one time line with different ids simultaneously.

Read the next example and you will understand the mix of different time lines:

XML DSL
<testcase name="StopTimeTest">
  <actions>
    <trace-time/>

    <trace-time id="time_line_id"/>

    <sleep seconds="3.5"/>

    <trace-time id=" time_line_id "/>

    <sleep milliseconds="5000"/>

    <trace-time/>

    <trace-time id=" time_line_id "/>
  </actions>
</testcase>
Java DSL
@CitrusTest
public void stopTimeTest() {
    stopTime();
    stopTime("time_line_id");
    sleep(3.5); // do something
    stopTime("time_line_id");
    sleep(5000); // do something
    stopTime();
    stopTime("time_line_id");
}

The test output looks like follows:

Starting TimeWatcher:
Starting TimeWatcher: time_line_id
TimeWatcher time_line_id after 3500 milliseconds
TimeWatcher after 8500 seconds
TimeWatcher time_line_id after 8500 milliseconds
Time line ids should not exist as test variables before the action is called for the first time. This would break the time line initialization.
In case no time line id is specified the framework will measure the time for a default time line. To print out the current elapsed time for a time line you simply have to place the `<trace-time> action into the action chain again and again, using the respective time line identifier. The elapsed time will be printed out to the console every time.

Each time line is stored as test variable in the test case. By default you will have the following test variables set for each time line:

CITRUS_TIMELINE

first timestamp of time line

CITRUS_TIMELINE_VALUE

latest time measurement value (time passed since first timestamp in milliseconds)

According to your time line id you will get different test variable names. Also you can customize the time value suffix (default: _VALUE):

XML DSL
<trace-time id="custom_watcher" suffix="_1st"/>
<sleep/>
<trace-time id="custom_watcher" suffix="_2nd"/>
Java DSL
@CitrusTest
stopTime("custom_watcher", "_1st");
sleep();
stopTime("custom_watcher", "_2nd");

You will get following test variables set:

custom_watcher

first timestamp of time line

custom_watcher_1st

time passed since start

custom_watcher_2nd

time passed since start

Of course using the same suffix multiple times will overwrite the timestamps in test variables.

13.9. Create variables

As you know variables usually are defined at the beginning of the test case (test-variables). It might also be helpful to reset existing variables as well as to define new variables during the test. The action <create-variables> is able to declare new variables or overwrite existing ones.

XML DSL
<testcase name="createVariablesTest">
    <variables>
        <variable name="myVariable" value="12345"/>
        <variable name="id" value="54321"/>
    </variables>
    <actions>
        <echo>
            <message>Current variable value: ${myVariable}</message>
        </echo>

        <create-variables>
            <variable name="myVariable" value="${id}"/>
            <variable name="newVariable" value="'this is a test'"/>
        </create-variables>

        <echo>
            <message>Current variable value: ${myVariable} </message>
        </echo>

        <echo>
            <message>
              New variable 'newVariable' has the value: ${newVariable}
            </message>
        </echo>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void createVariableTest() {
    variable("myVariable", "12345");
    variable("id", "54321");

    echo("Current variable value: ${myVariable}");

    createVariable("myVariable", "${id}");
    createVariable("newVariable", "this is a test");

    echo("Current variable value: ${myVariable}");

    echo("New variable 'newVariable' has the value: ${newVariable}");
}
Please note the difference between the variable() method and the createVariable() method. The first initializes the test case with the test variables. So all variables defined with this method are valid from the very beginning of the test. In contrary to that the createVariable() is executed within the test action chain. The newly created variables are then valid for the rest of the test. Trailing actions can reference the variables as usual with the variable expression.

13.10. Trace variables

You already know the <echo> action that prints messages to the console or logger. The <trace-variables> action is specially designed to trace all currently valid test variables to the console. This was mainly used by us for debug reasons. The usage is quite simple:

XML DSL
<testcase name="traceVariablesTest">
    <variables>
        <variable name="myVariable" value="12345"/>
        <variable name="nextVariable" value="54321"/>
    </variables>
    <actions>
        <trace-variables>
            <variable name="myVariable"/>
            <variable name="nextVariable"/>
        </trace-variables>

        <trace-variables/>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void traceTest() {
    variable("myVariable", "12345");
    variable("nextVariable", "54321");

    traceVariables("myVariable", "nextVariable");
    traceVariables();
}

Simply add the <trace-variables> action to your action chain and all variables will be printed out to the console. You are able to define a special set of variables by using the <variable> child elements. See the output that was generated by the test example above:

Current value of variable myVariable = 12345
Current value of variable nextVariable = 54321

13.11. Transform

The `<transform>` action transforms XML fragments with XSLT in order to construct various XML representations. The transformation result is stored into a test variable for further usage. The property xml-data defines the XML source, that is going to be transformed, while xslt-data defines the XSLT transformation rules. The attribute variable specifies the target test variable which receives the transformation result. The tester might use the action to transform XML messages as shown in the next code example:

XML DSL
  <testcase name="transformTest">
      <actions>
          <transform variable="result">
              <xml-data>
                  <![CDATA[
                      <TestRequest>
                          <Message>Hello World!</Message>
                      </TestRequest>
                  ]]>
              </xml-data>
              <xslt-data>
                  <![CDATA[
                      <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
                      <xsl:template match="/">
                          <html>
                              <body>
                                  <h2>Test Request</h2>
                                  <p>Message: <xsl:value-of select="TestRequest/Message"/></p>
                              </body>
                          </html>
                      </xsl:template>
                      </xsl:stylesheet>
                  ]]>
              </xslt-data>
          </transform>
          <echo>
              <message>${result}</message>
          </echo>
      </actions>
  </testcase>

The transformation above results to:

  <html>
      <body>
          <h2>Test Request</h2>
          <p>Message: Hello World!</p>
      </body>
  </html>

In the example we used CDATA sections to define the transformation source as well as the XSL transformation rules. As usual you can also use external file resources here. The transform action with external file resources looks like follows:

  <transform variable="result">
      <xml-resource file="classpath:transform-source.xml"/>
      <xslt-resource file="classpath:transform.xslt"/>
  </transform>

The Java DSL alternative for transforming data via XSTL in Citrus looks like follows:

Java DSL designer
@CitrusTest
public void transformTest() {
    transform()
        .source("<TestRequest>" +
                    "<Message>Hello World!</Message>" +
                "</TestRequest>")
        .xslt("<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n" +
                    "<xsl:template match=\"/\">\n" +
                    "<html>\n" +
                        "<body>\n" +
                            "<h2>Test Request</h2>\n" +
                            "<p>Message: <xsl:value-of select=\"TestRequest/Message\"/></p>\n" +
                        "</body>\n" +
                    "</html>\n" +
                    "</xsl:template>\n" +
                "</xsl:stylesheet>")
        .result("result");

    echo("${result}");

    transform()
        .source(new ClassPathResource("com/consol/citrus/actions/transform-source.xml"))
        .xslt(new ClassPathResource("com/consol/citrus/actions/transform.xslt"))
        .result("result");

    echo("${result}");
}
Java DSL runner
@CitrusTest
public void transformTest() {
    transform(action ->
        action.source("<TestRequest>" +
                        "<Message>Hello World!</Message>" +
                    "</TestRequest>")
        .xslt("<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n" +
                "<xsl:template match=\"/\">\n" +
                "<html>\n" +
                    "<body>\n" +
                        "<h2>Test Request</h2>\n" +
                        "<p>Message: <xsl:value-of select=\"TestRequest/Message\"/></p>\n" +
                    "</body>\n" +
                "</html>\n" +
                "</xsl:template>\n" +
            "</xsl:stylesheet>")
        .result("result"));

    echo("${result}");

    transform(action ->
        action.source(new ClassPathResource("com/consol/citrus/actions/transform-source.xml"))
              .xslt(new ClassPathResource("com/consol/citrus/actions/transform.xslt"))
              .result("result"));

    echo("${result}");
}

Defining multi-line Strings with nested quotes is no fun in Java. So you may want to use external file resources for your scripts as shown in the second part of the example. In fact you could also use script languages like Groovy or Scala that have much better support for multi-line Strings.

13.12. Groovy script execution

Groovy is an agile dynamic language for the Java Platform. Groovy ships with a lot of very powerful features and fits perfectly with Java as it is based on Java and runs inside the JVM.

The Citrus Groovy support might be the entrance for you to write customized test actions. You can easily execute Groovy code inside a test case, just like a normal test action. The whole test context with all variables is available to the Groovy action. This means someone can change variable values or create new variables very easily.

Let’s have a look at some examples in order to understand the possible Groovy code interactions in Citrus:

XML DSL
<testcase name="groovyTest">
  <variables>
    <variable name="time" value="citrus:currentDate()"/>
  </variables>
  <actions>
    <groovy>
        println 'Hello Citrus'
    </groovy>
    <groovy>
        println 'The variable is: ${time}'
    </groovy>
    <groovy resource="classpath:com/consol/citrus/script/example.groovy"/>
  </actions>
</testcase>
Java DSL designer
@CitrusTest
public void groovyTest() {
    groovy("println 'Hello Citrus'");
    groovy("println 'The variable is: ${time}'");

    groovy(new ClassPathResource("com/consol/citrus/script/example.groovy"));
}
Java DSL runner
@CitrusTest
public void groovyTest() {
    groovy(action -> action.script("println 'Hello Citrus'"));
    groovy(action -> action.script("println 'The variable is: ${time}'"));

    groovy(action -> action.script(new ClassPathResource("com/consol/citrus/script/example.groovy")));
}

As you can see it is possible to write Groovy code directly into the test case. Citrus will interpret and execute the Groovy code at runtime. As usual nested variable expressions are replaced with respective values. In general this is done in advance before the Groovy code is interpreted. For more complex Groovy code sections which grow in lines of code you can also reference external file resources.

After this basic Groovy code usage inside a test case we might be interested accessing the whole TestContext. The TestContext Java object holds all test variables and function definitions for the test case and can be referenced in Groovy code via simple naming convention. Just access the object reference 'context' and you are able to manipulate the TestContext (e.g. setting a new variable which is directly ready for use in following test actions).

XML DSL
<testcase name="groovyTest">
  <actions>
    <groovy>
      context.setVariable("greetingText","Hello Citrus")
      println context.getVariable("greetingText")
    </groovy>
    <echo>
      <message>New variable: ${greetingText}</message>
    </echo>
  </actions>
</testcase>
The implicit TestContext access that was shown in the previous sample works with a default Groovy script template provided by Citrus. The Groovy code you write in the test case is automatically surrounded with a Groovy script which takes care of handling the TestContext. The default template looks like follows:
import com.consol.citrus.*
import com.consol.citrus.variable.*
import com.consol.citrus.context.TestContext
import com.consol.citrus.script.GroovyAction.ScriptExecutor

public class GScript implements ScriptExecutor {
    public void execute(TestContext context) {
        @SCRIPTBODY@
    }
}

Your code is placed in substitution to the @SCRIPTBODY@ placeholder. Now you might understand how Citrus handles the context automatically. You can also write your own script templates making more advanced usage of other Java APIs and Groovy code. Just add a script template path to the test action like this:

<groovy script-template="classpath:my-custom-template.groovy">
  [...]
</groovy>

On the other hand you can disable the automatic script template wrapping in your action at all:

<groovy use-script-template="false">
  println 'Just use some Groovy code'
</groovy>

The next example deals with advanced Groovy code and writing whole classes. We write a new Groovy class which implements the ScriptExecutor interface offered by Citrus. This interface defines a special execute method and provides access to the whole TestContext for advanced test variables access.

<testcase name="groovyTest">
  <variables>
    <variable name="time" value="citrus:currentDate()"/>
  </variables>
  <actions>
    <groovy>
      <![CDATA[
        import com.consol.citrus.*
        import com.consol.citrus.variable.*
        import com.consol.citrus.context.TestContext
        import com.consol.citrus.script.GroovyAction.ScriptExecutor

        public class GScript implements ScriptExecutor {
            public void execute(TestContext context) {
                println context.getVariable("time")
            }
        }
      ]]>
    </groovy>
  </actions>
</testcase>

Implementing the ScriptExecutor interface in a custom Groovy class is applicable for very special test context manipulations as you are able to import and use other Java API classes in this code.

13.13. Failing the test

The fail action will generate an exception in order to terminate the test case with error. The test case will therefore not be successful in the reports.

The user can specify a custom error message for the exception in order to describe the error cause. Here is a very simple example to clarify the syntax:

XML DSL
<testcase name="failTest">
    <actions>
        <fail message="Test will fail with custom message"/>
    </actions>
</testcase>

Test results:

Execution of test: failTest failed! Nested exception is:
com.consol.citrus.exceptions.CitrusRuntimeException:
Test will fail with custom message

[...]

CITRUS TEST RESULTS

failTest          : failed - Exception is: Test will fail with custom message

Found 1 test cases to execute
Skipped 0 test cases (0.0%)
Executed 1 test cases, containing 3 actions
Tests failed:        1 (100.0%)
Tests successfully:  0 (0.0%)

While using the Java DSL tester might want to raise some Java exceptions in the middle of configuring the test case. But this is not possible as we have to separate the design time and the execution time of the test case. The @CitrusTest annotated configuration method is called for building up the whole test case. After this method was processed the test gets executed in runtime oth the test. If you specify a throws exception statement in the configuration method this will not be done at runtime but at design time. This is why you have to use the special fail test action which raises a Java exception during the runtime of the test. The next example will not work as expected:

Java DSL
@CitrusTest
public void wrongUsageSample() {
    // some test actions

    throw new ValidationException("This test should fail now"); // does not work as expected
}

The validation exception above is directly raised before the test is able to start as the @CitrusTest annotated method does not represent the test runtime. Instead of this we have to use the fail action as follows:

Java DSL
@CitrusTest
public void failTest() {
    // some test actions

    fail("This test should fail now"); // fails at test runtime as expected
}

Now the test fails at runtime as the fail action is raised during the test execution as expected.

13.14. Input

During the test case execution it is possible to read some user input from the command line. The test execution will stop and wait for keyboard inputs over the standard input stream. The user has to type the input and end it with the return key.

The user input is stored to the respective variable value.

XML DSL
<testcase name="inputTest">
    <variables>
        <variable name="userinput" value=""></variable>
        <variable name="userinput1" value=""></variable>
        <variable name="userinput2" value="y"></variable>
        <variable name="userinput3" value="yes"></variable>
        <variable name="userinput4" value=""></variable>
    </variables>
    <actions>
        <input/>
        <echo><message>user input was: ${userinput}</message></echo>

        <input message="Now press enter:" variable="userinput1"/>
        <echo><message>user input was: ${userinput1}</message></echo>

        <input message="Do you want to continue?"
                  valid-answers="y/n" variable="userinput2"/>
        <echo><message>user input was: ${userinput2}</message></echo>

        <input message="Do you want to continue?"
                  valid-answers="yes/no" variable="userinput3"/>
        <echo><message>user input was: ${userinput3}</message></echo>

        <input variable="userinput4"/>
        <echo><message>user input was: ${userinput4}</message></echo>
    </actions>
</testcase>

As you can see the input action is customizable with a prompt message that is displayed to the user and some valid answer possibilities. The user input is stored to a test variable for further use in the test case. In detail the input action offers following attributes:

message

message displayed to the user

valid-answers

possible valid answers separated with '/' character

variable

result variable name holding the user input (default = ${userinput})

The same action in Java DSL now looks quite familiar to us although attribute naming is slightly different:

Java DSL designer
@CitrusTest
public void inputActionTest() {
    variable("userinput", "");
    variable("userinput1", "");
    variable("userinput2", "y");
    variable("userinput3", "yes");
    variable("userinput4", "");

    input();
    echo("user input was: ${userinput}");
    input().message("Now press enter:").result("userinput1");
    echo("user input was: ${userinput1}");
    input().message("Do you want to continue?").answers("y", "n").result("userinput2");
    echo("user input was: ${userinput2}");
    input().message("Do you want to continue?").answers("yes", "no").result("userinput3");
    echo("user input was: ${userinput3}");
    input().result("userinput4");
    echo("user input was: ${userinput4}");
}
Java DSL runner
@CitrusTest
public void inputActionTest() {
    variable("userinput", "");
    variable("userinput1", "");
    variable("userinput2", "y");
    variable("userinput3", "yes");
    variable("userinput4", "");

    input(action -> {});
    echo("user input was: ${userinput}");
    input(action -> action.message("Now press enter:").result("userinput1"));
    echo("user input was: ${userinput1}");
    input(action -> action.message("Do you want to continue?").answers("y", "n").result("userinput2"));
    echo("user input was: ${userinput2}");
    input(action -> action.message("Do you want to continue?").answers("yes", "no").result("userinput3"));
    echo("user input was: ${userinput3}");
    input(action -> action.result("userinput4"));
    echo("user input was: ${userinput4}");
}

When the user input is restricted to a set of valid answers the input validation of course can fail due to mismatch. This is the case when the user provides some input not matching the valid answers given. In this case the user is again asked to provide valid input. The test action will continue to ask for valid input until a valid answer is given.

User inputs may not fit to automatic testing in terms of continuous integration testing where no user is present to type in the correct answer over the keyboard. In this case you can always skip the user input in advance by specifying a variable that matches the user input variable name. As the user input variable is then already present the user input is missed out and the test proceeds automatically.

13.15. Load

You are able to load properties from external property files and store them as test variables. The action will require a file resource either from class path or file system in order to read the property values.

Let us look at an example to get an idea about this action:

Content of load.properties
username=Mickey Mouse
greeting.text=Hello Test Framework
XML DSL
<testcase name="loadPropertiesTest">
    <actions>
        <load>
            <properties file="file:tests/resources/load.properties"/>
        </load>

        <trace-variables/>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void loadPropertiesTest() {
    load("file:tests/resources/load.properties");

    traceVariables();
}
Output
Current value of variable username = Mickey Mouse
Current value of variable greeting.text = Hello Test Framework

The action will load all available properties in the file load.properties and store them to the test case as local variables.

Please be aware of the fact that existing variables are overwritten!

13.16. Wait

With this action you can make your test wait until a certain condition is satisfied. The attribute seconds defines the amount of time to wait in seconds. You can also use the milliseconds attribute for a more fine grained time value. The attribute interval defines the amount of time to wait between each check. The interval is always specified as millisecond time interval.

If the check does not exceed within the defined overall waiting time then the test execution fails with an appropriate error message. There are different types of conditions to check.

http

This condition is based on a Http request call on a server endpoint. Citrus will wait until the Http response is as defined (e.g. Http 200 OK). This is useful when you want to wait for a server to start.

file

This condition checks for the existence of a file on the local file system. Citrus will wait until the file is present.

message

This condition checks for the existence of a message in the local message store of the current test case. Citrus will wait until the message with the given name is present.

Next let us have a look at a simple example:

XML DSL
<testcase name="waitTest">
    <actions>
        <wait seconds="10" interval="2000" >
          <http url="http://sample.org/resource" statusCode="200" timeout="2000" />
        <wait/>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void waitTest() {
    waitFor().http("http://sample.org/resource").seconds(10L).interval(2000L);
}

The example waits for some Http server resource to be available with Http 200 OK response. Citrus will use HEAD request method by default. You can set the request method with the method attribute on the Http condition.

Next let us have a look at the file condition usage:

XML DSL
<testcase name="waitTest">
    <actions>
        <wait seconds="10" interval="2000" >
          <file path="path/to/resource/file.txt" />
        <wait/>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void waitTest() {
    waitFor().file("path/to/resource/file.txt");
}

Citrus checks for the file to exist under the given path. Only if the file exists the test will continue with further test actions.

Next let us have a look at the message condition usage:

XML DSL
<testcase name="waitTest">
    <actions>
        <wait seconds="10" interval="2000" >
          <message name="helloRequest" />
        <wait/>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void waitTest() {
    waitFor().message("helloRequest");
}

Citrus checks for the message with the name helloRequest in the local message store. Only if the message with the given name is found the test will continue with further test actions. The local message store is automatically filled with all exchanged messages (send or receive) in a test case. The message names are defined in the respective send or receive operations in the test.

When should somebody use this action? This action is very useful when you want your test to wait for a certain event to occur before continuing with the test execution. For example if you wish that your test waits until a Docker container is started or for an application to create a log file before continuing, then use this action. You can also create your own condition statements and bind it to the test action.

13.17. Purging JMS destinations

Purging JMS destinations during the test run is quite essential. Different test cases can influence each other when sending messages to the same JMS destinations. A test case should only receive those messages that actually belong to it. Therefore it is a good idea to purge all JMS queue destinations between the test cases. Obsolete messages that are stuck in a JMS queue for some reason are then removed so that the following test case is not offended.

Citrus provides special support for JMS related features. We have to activate those JMS features in our test case by adding a special "jms" namespace and schema definition location to the test case XML.
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
        xmlns:spring="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:jms="http://www.citrusframework.org/schema/jms/testcase"
        xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.citrusframework.org/schema/testcase
        http://www.citrusframework.org/schema/testcase/citrus-testcase.xsd
        http://www.citrusframework.org/schema/jms/testcase
        http://www.citrusframework.org/schema/jms/testcase/citrus-jms-testcase.xsd">

    [...]

</beans>

Now we are ready to use the JMS features in our test case in order to purge some JMS queues. This can be done with following action definition:

XML DSL
<testcase name="purgeTest">
  <actions>
      <jms:purge-jms-queues>
          <jms:queue name="Some.JMS.QUEUE.Name"/>
          <jms:queue name="Another.JMS.QUEUE.Name"/>
          <jms:queue name="My.JMS.QUEUE.Name"/>
      </jms:purge-jms-queues>

      <jms:purge-jms-queues connection-factory="connectionFactory">
          <jms:queue name="Some.JMS.QUEUE.Name"/>
          <jms:queue name="Another.JMS.QUEUE.Name"/>
          <jms:queue name="My.JMS.QUEUE.Name"/>
      </jms:purge-jms-queues>
  </actions>
</testcase>

Notice that we have referenced the jms namespace when using the purge-jms-queues test action.

Java DSL designer
@Autowired
@Qualifier("connectionFactory")
private ConnectionFactory connectionFactory;

@CitrusTest
public void purgeTest() {
    purgeQueues()
        .queue("Some.JMS.QUEUE.Name")
        .queue("Another.JMS.QUEUE.Name");

    purgeQueues(connectionFactory)
        .timeout(150L) // custom timeout in ms
        .queue("Some.JMS.QUEUE.Name")
        .queue("Another.JMS.QUEUE.Name");
}
Java DSL runner
@Autowired
@Qualifier("connectionFactory")
private ConnectionFactory connectionFactory;

@CitrusTest
public void purgeTest() {
    purgeQueues(action ->
        action.queue("Some.JMS.QUEUE.Name")
            .queue("Another.JMS.QUEUE.Name"));

    purgeQueues(action -> action.connectionFactory(connectionFactory)
            .timeout(150L) // custom timeout in ms
            .queue("Some.JMS.QUEUE.Name")
            .queue("Another.JMS.QUEUE.Name"));
}

Purging the JMS queues in every test case is quite exhausting because every test case needs to define a purging action at the very beginning of the test. Fortunately the test suite definition offers tasks to run before, between and after the test cases which should ease up this tasks a lot. The test suite offers a very simple way to purge the destinations between the tests. See testsuite-before-testfor more information about this.

As you can see in the next example it is quite easy to specify a group of destinations in the Spring configuration that get purged before a test is executed.

<citrus:before-test id="purgeBeforeTest">
    <citrus:actions>
        <jms:purge-jms-queues>
            <jms:queue name="Some.JMS.QUEUE.Name"/>
            <jms:queue name="Another.JMS.QUEUE.Name"/>
        </jms:purge-jms-queues>
    </citrus:actions>
</citrus:before-test>
Please keep in mind that the JMS related configuration components in Citrus belong to a separate XML namespace jms: . We have to add this namespace declaration to each test case XML and Spring bean XML configuration file as described at the very beginning of this section.

The syntax for purging the destinations is the same as we used it inside the test case. So now we are able to purge JMS destinations with given destination names. But sometimes we do not want to rely on queue or topic names as we retrieve destinations over JNDI for instance. We can deal with destinations coming from JNDI lookup like follows:

<jee:jndi-lookup id="jmsQueueHelloRequestIn" jndi-name="jms/jmsQueueHelloRequestIn"/>
<jee:jndi-lookup id="jmsQueueHelloResponseOut" jndi-name="jms/jmsQueueHelloResponseOut"/>

<citrus:before-test id="purgeBeforeTest">
    <citrus:actions>
        <jms:purge-jms-queues>
            <jms:queue ref="jmsQueueHelloRequestIn"/>
            <jms:queue ref="jmsQueueHelloResponseOut"/>
        </jms:purge-jms-queues>
    </citrus:actions>
</citrus:before-test>

We just use the attribute 'ref' instead of 'name' and Citrus is looking for a bean reference for that identifier that resolves to a JMS destination. You can use the JNDI bean references inside a test case, too.

XML DSL
<testcase name="purgeTest">
  <actions>
      <jms:purge-jms-queues>
          <jms:queue ref="jmsQueueHelloRequestIn"/>
          <jms:queue ref="jmsQueueHelloResponseOut"/>
      </jms:purge-jms-queues>
  </actions>
</testcase>

Of course you can use queue object references also in Java DSL test cases. Here we easily can use Spring’s dependency injection with autowiring to get the object references from the IoC container.

Java DSL designer
@Autowired
@Qualifier("jmsQueueHelloRequestIn")
private Queue jmsQueueHelloRequestIn;

@Autowired
@Qualifier("jmsQueueHelloResponseOut")
private Queue jmsQueueHelloResponseOut;

@CitrusTest
public void purgeTest() {
    purgeQueues()
        .queue(jmsQueueHelloRequestIn)
        .queue(jmsQueueHelloResponseOut);
}
Java DSL runner
@Autowired
@Qualifier("jmsQueueHelloRequestIn")
private Queue jmsQueueHelloRequestIn;

@Autowired
@Qualifier("jmsQueueHelloResponseOut")
private Queue jmsQueueHelloResponseOut;

@CitrusTest
public void purgeTest() {
    purgeQueues(action ->
        action.queue(jmsQueueHelloRequestIn)
            .queue(jmsQueueHelloResponseOut));
}
You can mix queue name and queue object references as you like within one single purge queue test action.

13.18. Purging message channels

Message channels define central messaging destinations in Citrus. These are namely in memory message queues holding messages for test cases. These messages may become obsolete during a test run, especially when test cases fail and stop in their message consumption. Purging these message channel destinations is essential in these scenarios in order to not influence upcoming test cases. Each test case should only receive those messages that actually refer to the test model. Therefore it is a good idea to purge all message channel destinations between the test cases. Obsolete messages that get stuck in a message channel destination for some reason are then removed so that upcoming test case are not broken.

Following action definition purges all messages from a list of message channels:

XML DSL
<testcase name="purgeChannelTest">
  <actions>
      <purge-channel>
          <channel name="someChannelName"/>
          <channel name="anotherChannelName"/>
      </purge-channel>

      <purge-channel>
          <channel ref="someChannel"/>
          <channel ref="anotherChannel"/>
      </purge-channel>
  </actions>
</testcase>

As you can see the test action supports channel names as well as channel references to Spring bean instances. When using channel references you refer to the Spring bean id or name in your application context.

The Java DSL works quite similar as you can read from next examples:

Java DSL designer
@Autowired
@Qualifier("channelResolver")
private DestinationResolver<MessageChannel> channelResolver;

@CitrusTest
public void purgeTest() {
    purgeChannels()
        .channelResolver(channelResolver)
        .channelNames("ch1", "ch2", "ch3")
        .channel("ch4");
}
Java DSL runner
@Autowired
@Qualifier("channelResolver")
private DestinationResolver<MessageChannel> channelResolver;

@CitrusTest
public void purgeTest() {
    purgeChannels(action ->
        action.channelResolver(channelResolver)
                .channelNames("ch1", "ch2", "ch3")
                .channel("ch4"));
}

The channel resolver reference is optional. By default Citrus will automatically use a Spring application context channel resolver so you just have to use the respective Spring bean names that are configured in the Spring application context. However setting a custom channel resolver may be adequate for you in some special cases.

While speaking of Spring application context bean references the next example uses such bean references for channels to purge.

Java DSL designer
@Autowired
@Qualifier("channel1")
private MessageChannel channel1;

@Autowired
@Qualifier("channel2")
private MessageChannel channel2;

@Autowired
@Qualifier("channel3")
private MessageChannel channel3;

@CitrusTest
public void purgeTest() {
    purgeChannels()
        .channels(channel1, channel2)
        .channel(channel3);
}
Java DSL runner
@Autowired
@Qualifier("channel1")
private MessageChannel channel1;

@Autowired
@Qualifier("channel2")
private MessageChannel channel2;

@Autowired
@Qualifier("channel3")
private MessageChannel channel3;

@CitrusTest
public void purgeTest() {
    purgeChannels(action ->
        action.channels(channel1, channel2)
                .channel(channel3));
}

Message selectors enable you to selectively remove messages from the destination. All messages that pass the message selection logic get deleted the other messages will remain unchanged inside the channel destination. The message selector is a Spring bean that implements a special message selector interface. A possible implementation could be a selector deleting all messages that are older than five seconds:

import org.springframework.messaging.Message;
import org.springframework.integration.core.MessageSelector;

public class TimeBasedMessageSelector implements MessageSelector {

    public boolean accept(Message<?> message) {
        if (System.currentTimeMillis() - message.getHeaders().getTimestamp() > 5000) {
            return false;
        } else {
            return true;
        }
    }

}
The message selector returns false for those messages that should be deleted from the channel!

You simply define the message selector as a new Spring bean in the Citrus application context and reference it in your test action property.

<bean id="specialMessageSelector"
    class="com.consol.citrus.special.TimeBasedMessageSelector"/>

Now let us have a look at how you reference the selector in your test case:

XML DSL
<purge-channels message-selector="specialMessageSelector">
  <channel name="someChannelName"/>
  <channel name="anotherChannelName"/>
</purge-channels>
Java DSL designer
@Autowired
@Qualifier("specialMessageSelector")
private MessageSelector specialMessageSelector;

@CitrusTest
public void purgeTest() {
    purgeChannels()
        .channelNames("ch1", "ch2", "ch3")
        .selector(specialMessageSelector);
}
Java DSL runner
@Autowired
@Qualifier("specialMessageSelector")
private MessageSelector specialMessageSelector;

@CitrusTest
public void purgeTest() {
    purgeChannels(action ->
        action.channelNames("ch1", "ch2", "ch3")
                .selector(specialMessageSelector));
}

In the examples above we use a message selector implementation that gets injected via Spring IoC container.

Purging channels in each test case every time is quite exhausting because every test case needs to define a purging action at the very beginning of the test. A more straight forward approach would be to introduce some purging action which is automatically executed before each test. Fortunately the Citrus test suite offers a very simple way to do this. It is described in testsuite-before-test.

When using the special action sequence before test cases we are able to purge channel destinations every time a test case executes. See the upcoming example to find out how the action is defined in the Spring configuration application context.

<citrus:before-test id="purgeBeforeTest">
    <citrus:actions>
        <purge-channel>
            <channel name="fooChannel"/>
            <channel name="barChannel"/>
        </purge-channel>
    </citrus:actions>
</citrus:before-test>

Just use this before-test bean in the Spring bean application context and the purge channel action is active. Obsolete messages that are waiting on the message channels for consumption are purged before the next test in line is executed.

Purging message channels becomes also very interesting when working with server instances in Citrus. Each server component automatically has an inbound message channel where incoming messages are stored to internally. So if you need to clean up a server that has already stored some incoming messages you can do this easily by purging the internal message channel. The message channel follows a naming convention {serverName}.inbound where {serverName} is the Spring bean name of the Citrus server endpoint component. If you purge this internal channel in a before test nature you are sure that obsolete messages on a server instance get purged before each test is executed.

13.19. Purging endpoints

Citrus works with message endpoints when sending and receiving messages. In general endpoints can also queue messages. This is especially the case when using JMS message endpoints or any server endpoint component in Citrus. These are in memory message queues holding messages for test cases. These messages may become obsolete during a test run, especially when a test case that would consume the messages fails. Deleting all messages from a message endpoint is therefore a useful task and is essential in such scenarios so that upcoming test cases are not influenced. Each test case should only receive those messages that actually refer to the test model. Therefore it is a good idea to purge all message endpoint destinations between the test cases. Obsolete messages that get stuck in a message endpoint destination for some reason are then removed so that upcoming test case are not broken.

Following action definition purges all messages from a list of message endpoints:

XML DSL
<testcase name="purgeEndpointTest">
  <actions>
      <purge-endpoint>
          <endpoint name="someEndpointName"/>
          <endpoint name="anotherEndpointName"/>
      </purge-endpoint>

      <purge-endpoint>
          <endpoint ref="someEndpoint"/>
          <endpoint ref="anotherEndpoint"/>
      </purge-endpoint>
  </actions>
</testcase>

As you can see the test action supports endpoint names as well as endpoint references to Spring bean instances. When using endpoint references you refer to the Spring bean name in your application context.

The Java DSL works quite similar - have a look:

Java DSL designer
@CitrusTest
public void purgeTest() {
    purgeEndpoints()
        .endpointNames("endpoint1", "endpoint2", "endpoint3")
        .endpoint("endpoint4");
}
Java DSL runner
@CitrusTest
public void purgeTest() {
    purgeEndpoints(action ->
        action.endpointNames("endpoint1", "endpoint2", "endpoint3")
                .endpoint("endpoint4"));
}

When using the Java DSL we can inject endpoint objects with Spring bean container IoC. The next example uses such bean references for endpoints in a purge action.

Java DSL designer
@Autowired
@Qualifier("endpoint1")
private Endpoint endpoint1;

@Autowired
@Qualifier("endpoint2")
private Endpoint endpoint2;

@Autowired
@Qualifier("endpoint3")
private Endpoint endpoint3;

@CitrusTest
public void purgeTest() {
    purgeEndpoints()
        .endpoints(endpoint1, endpoint2)
        .endpoint(endpoint3);
}
Java DSL runner
@Autowired
@Qualifier("endpoint1")
private Endpoint endpoint1;

@Autowired
@Qualifier("endpoint2")
private Endpoint endpoint2;

@Autowired
@Qualifier("endpoint3")
private Endpoint endpoint3;

@CitrusTest
public void purgeTest() {
    purgeEndpoints(action ->
        action.endpoints(endpoint1, endpoint2)
                .endpoint(endpoint3));
}

Message selectors enable you to selectively remove messages from an endpoint. All messages that meet the message selector condition get deleted and the other messages remain inside the endpoint destination. The message selector is either a normal String name-value representation or a map of key value pairs:

XML DSL
<purge-endpoints>
  <selector>
    <value>operation = 'sayHello'</value>
  </selector>
  <endpoint name="someEndpointName"/>
  <endpoint name="anotherEndpointName"/>
</purge-endpoints>
Java DSL designer
@CitrusTest
public void purgeTest() {
    purgeEndpoints()
        .endpointNames("endpoint1", "endpoint2", "endpoint3")
        .selector("operation = 'sayHello'");
}
Java DSL runner
@CitrusTest
public void purgeTest() {
    purgeEndpoints(action ->
        action.endpointNames("endpoint1", "endpoint2", "endpoint3")
                .selector("operation = 'sayHello'"));
}

In the examples above we use a String to represent the message selector expression. In general the message selector operates on the message header. So following on from that we remove all messages selectively that have a message header operation with its value sayHello .

Purging endpoints in each test case every time is quite exhausting because every test case needs to define a purging action at the very beginning of the test. A more straight forward approach would be to introduce some purging action which is automatically executed before each test. Fortunately the Citrus test suite offers a very simple way to do this. It is described in testsuite-before-test.

When using the special action sequence before test cases we are able to purge endpoint destinations every time a test case executes. See the upcoming example to find out how the action is defined in the Spring configuration application context.

<citrus:before-test id="purgeBeforeTest">
    <citrus:actions>
        <purge-endpoint>
            <endpoint name="fooEndpoint"/>
            <endpoint name="barEndpoint"/>
        </purge-endpoint>
    </citrus:actions>
</citrus:before-test>

Just use this before-test bean in the Spring bean application context and the purge endpoint action is active. Obsolete messages that are waiting on the message endpoints for consumption are purged before the next test in line is executed.

Purging message endpoints becomes also very interesting when working with server instances in Citrus. Each server component automatically has an inbound message endpoint where incoming messages are stored to internally. Citrus will automatically use this incoming message endpoint as target for the purge action so you can just use the server instance as you know it from your configuration in any purge action.

13.20. Assert failure

Citrus test actions fail with Java exceptions and error messages. This gives you the opportunity to expect an action to fail during test execution. You can simple assert a Java exception to be thrown during execution. See the example for an assert action definition in a test case:

XML DSL
<testcase name="assertFailureTest">
    <actions>
        <assert exception="com.consol.citrus.exceptions.CitrusRuntimeException"
                   message="Unknown variable ${date}">
            <when>
                <echo>
                    <message>Current date is: ${date}</message>
                </echo>
            </when>
        </assert>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void assertTest() {
    assertException().exception(com.consol.citrus.exceptions.CitrusRuntimeException.class)
                     .message("Unknown variable ${date}")
                .when(echo("Current date is: ${date}"));
}
Note that the assert action requires an exception. In case no exception is thrown by the embedded test action the assertion and the test case will fail!

The assert action always wraps a single test action, which is then monitored for failure. In case the nested test action fails with error you can validate the error in its type and error message (optional). The failure has to fit the expected one exactly otherwise the assertion fails itself.

Important to notice is the fact that asserted exceptions do not cause failure of the test case. As you except the failure to happen the test continues with its work once the assertion is done successfully.

13.21. Catch exceptions

In the previous chapter we have seen how to expect failures in Citrus with assert action. Now the assert action is designed for single actions to be monitored and for failures to be expected in any case. The 'catch' action in contrary can hold several nested test actions and exception failure is optional.

The nested actions are error proof for the chosen exception type. This means possible exceptions are caught and ignored - the test case will not fail for this exception type. But only for this particular exception type! Other exception types that occur during execution do cause the test to fail as usual.

XML DSL
<testcase name="catchExceptionTest">
    <actions>
        <catch exception="com.consol.citrus.exceptions.CitrusRuntimeException">
            <echo>
                <message>Current date is: ${date}</message>
            </echo>
        </catch>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void catchTest() {
    catchException().exception(CitrusRuntimeException.class)
                    .when(echo("Current date is: ${date}"));
}
Note that there is no validation available in a catch block. So catching exceptions is just to make a test more stable towards errors that can occur. The caught exception does not cause any failure in the test. The test case may continue with execution as if there was not failure. Also notice that the catch action is also happy when no exception at all is raised. In contrary to that the assert action requires the exception and an assert action is failing in positive processing.

Catching exceptions like this may only fit to very error prone action blocks where failures do not harm the test case success. Otherwise a failure in a test action should always reflect to the whole test case to fail with errors.

Java developers might ask why not use try-catch Java block instead? The answer is simple yet very important to understand. The test method is called by the Java DSL test case builder for building the Citrus test. This can be referred to as the design time of the test. After the building test method was processed the test gets executed, which can be called the runtime of the test. This means that a try-catch block within the design time method will never perform during the test run. The only reliable way to add the catch capability to the test as part of the test case runtime is to use the Citrus test action which gets executed during test runtime.

13.22. Apache Ant build

The <ant> action loads a build.xml Ant file and executes one or more targets in the Ant project. The target is executed with optional build properties passed to the Ant run. The Ant build output is logged with Citrus logger and the test case success is bound to the Ant build success. This means in case the Ant build fails for some reason the test case will also fail with build exception accordingly.

See this basic Ant run example to see how it works within your test case:

XML DSL
<testcase name="AntRunTest">
    <variables>
        <variable name="today" value="citrus:currentDate()"/>
    </variables>
    <actions>
        <ant build-file="classpath:com/consol/citrus/actions/build.xml">
            <execute target="sayHello"/>
            <properties>
                <property name="date" value="${today}"/>
                <property name="welcomeText" value="Hello!"/>
            </properties>
        </ant>
    </actions>
</testcase>
Java DSL designer
@CitrusTest
public void antRunTest() {
    variable("today", "citrus:currentDate()");

    antrun("classpath:com/consol/citrus/actions/build.xml")
        .target("sayHello")
        .property("date", "${today}")
        .property("welcomeText", "$Hello!");
}
Java DSL runner
@CitrusTest
public void antRunTest() {
    variable("today", "citrus:currentDate()");

    antrun(action -> action.buildFilePath("classpath:com/consol/citrus/actions/build.xml")
                .target("sayHello")
                .property("date", "${today}")
                .property("welcomeText", "$Hello!"));
}

The respective build.xml Ant file must provide the target to call. For example:

<project name="citrus-build" default="sayHello">
    <property name="welcomeText" value="Welcome to Citrus!"></property>

    <target name="sayHello">
        <echo message="${welcomeText} - Today is ${date}"></echo>
    </target>

    <target name="sayGoodbye">
        <echo message="Goodbye everybody!"></echo>
    </target>
</project>

As you can see you can pass custom build properties to the Ant build execution. Existing Ant build properties are replaced and you can use the properties in your build file as usual.

You can also call multiple targets within one single build run by using a comma separated list of target names:

XML DSL
<testcase name="AntRunTest">
    <variables>
        <variable name="today" value="citrus:currentDate()"/>
    </variables>
    <actions>
        <ant build-file="classpath:com/consol/citrus/actions/build.xml">
            <execute targets="sayHello,sayGoodbye"/>
            <properties>
                <property name="date" value="${today}"/>
            </properties>
        </ant>
    </actions>
</testcase>
Java DSL designer
@CitrusTest
public void antRunTest() {
    variable("today", "citrus:currentDate()");

    antrun("classpath:com/consol/citrus/actions/build.xml")
        .targets("sayHello", "sayGoodbye")
        .property("date", "${today}");
}
Java DSL runner
@CitrusTest
public void antRunTest() {
    variable("today", "citrus:currentDate()");

    antrun(action -> action.buildFilePath("classpath:com/consol/citrus/actions/build.xml")
                .targets("sayHello", "sayGoodbye")
                .property("date", "${today}"));
}

The build properties can live in external file resource as an alternative to the inline property definitions. You just have to use the respective file resource path and all nested properties get loaded as build properties.

In addition to that you can also define a custom build listener. The build listener must implement the Ant API interface org.apache.tools.ant.BuildListener . During the Ant build run the build listener is called with several callback methods (e.g. buildStarted(), buildFinished(), targetStarted(), targetFinished(), …). This is how you can add additional logic to the Ant build run from Citrus. A custom build listener could manage the fail state of your test case, in particular by raising some exception forcing the test case to fail accordingly.

XML DSL
<testcase name="AntRunTest">
    <actions>
        <ant build-file="classpath:com/consol/citrus/actions/build.xml"
                build-listener="customBuildListener">
            <execute target="sayHello"/>
            <properties file="classpath:com/consol/citrus/actions/build.properties"/>
        </ant>
    </actions>
</testcase>
Java DSL designer
@Autowired
private BuildListener customBuildListener;

@CitrusTest
public void antRunTest() {
    antrun("classpath:com/consol/citrus/actions/build.xml")
        .target("sayHello")
        .propertyFile("classpath:com/consol/citrus/actions/build.properties")
        .listener(customBuildListener);
}
Java DSL runner
@Autowired
private BuildListener customBuildListener;

@CitrusTest
public void antRunTest() {
    antrun(action -> action.buildFilePath("classpath:com/consol/citrus/actions/build.xml")
            .target("sayHello")
            .propertyFile("classpath:com/consol/citrus/actions/build.properties")
            .listener(customBuildListener));
}

The customBuildListener used in the example above should reference a Spring bean in the Citrus application context. The bean implements the interface org.apache.tools.ant.BuildListener and controls the Ant build run.

13.23. Start/Stop server

Citrus is working with server components that are started and stopped within a test run. This can be a Http server or some SMTP mail server for instance. Usually the Citrus server components are automatically started when Citrus is starting and respectively stopped when Citrus is shutting down. Sometimes it might be helpful to explicitly start and stop a server instance within your test case. Here you can use special start and stop test actions inside your test. This is a good way to test downtime scenarios of interface partners with respective error handling when connections to servers are lost

Let me explain with a simple sample test case:

XML DSL
<testcase name="sleepTest">
    <actions>
        <start server="myMailServer"/>

        <sleep/>

        <stop server="myMailServer"/>
    </actions>
</testcase>

The start and stop server test action receive a server name which references a Spring bean component of type com.consol.citrus.server.Server in your basic Spring application context. The server instance is started or stopped within the test case. As you can see in the next listing we can also start and stop multiple server instances within a single test action.

<testcase name="sleepTest">
    <actions>
        <start>
            <servers>
                <server name="myMailServer"/>
                <server name="myFtpServer"/>
            </servers>
        </start>

        <sleep/>

        <stop>
            <servers>
                <server name="myMailServer"/>
                <server name="myFtpServer"/>
            </servers>
        </stop>
    </actions>
</testcase>

When using the Java DSL the best way to reference a server instance is to autowire the Spring bean via dependency injection. The Spring framework takes case on injecting the proper Spring bean component defined in the Spring application context. This way you can easily start and stop server instances within Java DSL test cases.

Java DSL
@Autowired
@Qualifier("myFtpServer")
private FtpServer myFtpServer;

@CitrusTest
public void startStopServerTest() {
    start(myFtpServer);

    sleep();

    stop(myFtpServer);
}
Starting and stopping server instances is a synchronous test action. This means that your test case is waiting for the server to start before other test actions take place. Startup times and shut down of server instances may delay your test accordingly.

As you can see starting and stopping Citrus server instances is very easy. You can also write your own server implementations by implementing the interface com.consol.citrus.server.Server . All custom server implementations can then be started and stopped during a test case.

13.24. Stop Timer

The <stop-timer> action can be used for stopping either a specific timer (containers-timer) or all timers running within a test. This action is useful when timers are started in the background (using parallel or fork=true) and you wish to stop these timers at the end of the test. Some examples of using this action are provided below:

XML DSL
<testcase name="timerTest">
    <actions>
      <timer id="forkedTimer" fork="true">
        <sleep milliseconds="50" />
      </timer>

      <timer fork="true">
        <sleep milliseconds="50" />
      </timer>

      <timer repeatCount="5">
        <sleep milliseconds="50" />
      </timer>

      <stop-timer timerId="forkedTimer" />
    </actions>
    <finally>
      <stop-timer />
    </finally>
  </testcase>
Java DSL
@CitrusTest
  public void timerTest() {

    timer()
      .timerId("forkedTimer")
      .fork(true)
      .actions(sleep(50L)
    );

    timer()
      .fork(true)
      .actions(sleep(50L)
    );

    timer()
      .repeatCount(5)
      .actions(sleep(50L));

    stopTimer("forkedTimer")

    doFinally().actions(
      stopTimer()
    );
    }

In the above example 3 timers are started, the first 2 in the background and the third in the test execution thread. Timer #3 has a repeatCount set to 5 so it will terminate automatically after 5 runs. Timer #1 and #2 however have no repeatCount set so they will execute until they are told to stop.

Timer #1 is stopped explicitly using the first stopTimer action. Here the stopTimer action includes the name of the timer to stop. This is convenient when you wish to terminate a specific timer. However since no timerId was set for timer #2, you can terminate this (and all other timers) using the 'stopTimer' action with no explicit timerId set.

13.25. Custom test actions

Now we have a look at the opportunity to add custom test actions to the test case flow. Let us start this section with an example:

XML DSL
<testcase name="ActionReferenceTest">
    <actions>
        <action reference="cleanUpDatabase"/>
        <action reference="mySpecialAction"/>
    </actions>
</testcase>

The generic <action> element references Spring beans that implement the Java interface com.consol.citrus.TestAction . This is a very fast way to add your own action implementations to a Citrus test case. This way you can easily implement your own actions in Java and include them into the test case.

In the example above the called actions are special database cleanup implementations. The actions are defined as Spring beans in the Citrus configuration and get referenced by their bean name or id.

<bean id="cleanUpDatabase" class="my.domain.citrus.actions.SpecialDatabaseCleanupAction">
    <property name="dataSource" ref="testDataSource"/>
</bean>

The Spring application context holds your custom bean implementations. You can set properties and use the full Spring power while implementing your custom test action in Java. Let us have a look on how such a Java class may look like.

import com.consol.citrus.actions.AbstractTestAction;
import com.consol.citrus.context.TestContext;

public class SpecialDatabaseCleanupAction extends AbstractTestAction {

    @Autowired
    private DataSource dataSource;

    @Override
    public void doExecute(TestContext context) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

        jdbcTemplate.execute("...");
    }

}

All you need to do in your Java class is to implement the Citrus com.consol.citrus.TestAction interface. The abstract class com.consol.citrus.actions.AbstractTestAction may help you to start with your custom test action implementation as it provides basic method implementations so you just have to implement the doExecute() method.

When using the Java test case DSL you are also quite comfortable with including your custom test actions.

Java DSL
@Autowired
private SpecialDatabaseCleanupAction cleanUpDatabaseAction;

@CitrusTest
public void genericActionTest() {
    echo("Now let's include our special test action");

    action(cleanUpDatabaseAction);

    echo("That's it!");
}

Using anonymous class implementations is also possible.

Java DSL
@CitrusTest
public void genericActionTest() {
    echo("Now let's call our special test action anonymously");

    action(new AbstractTestAction() {
        public void doExecute(TestContext context) {
            // do something
        }
    });

    echo("That's it!");
}

14. Templates

Templates group action sequences to a logical unit. You can think of templates as reusable components that are used in several tests. The maintenance is much more effective because the templates are referenced several times.

The template always has a unique name. Inside a test case we call the template by this unique name. Have a look at a first example:

<template name="doCreateVariables">
    <create-variables>
        <variable name="var" value="123456789"/>
    </create-variables>

    <call-template name="doTraceVariables"/>
</template>

<template name="doTraceVariables">
    <echo>
        <message>Current time is: ${time}</message>
    </echo>

    <trace-variables/>
</template>

The code example above describes two template definitions. Templates hold a sequence of test actions or call other templates themselves as seen in the example above.

The <call-template> action calls other templates by their name. The called template not necessarily has to be located in the same test case XML file. The template might be defined in a separate XML file other than the test case itself:
XML DSL
<testcase name="templateTest">
    <variables>
        <variable name="myTime" value="citrus:currentDate()"/>
    </variables>
    <actions>
        <call-template name="doCreateVariables"/>

        <call-template name="doTraceVariables">
            <parameter name="time" value="${myTime}">
        </call-template>
    </actions>
</testcase>
Java DSL designer
@CitrusTest
public void templateTest() {
    variable("myTime", "citrus:currentDate()");

    applyTemplate("doCreateVariables");

    applyTemplate("doTraceVariables")
        .parameter("time", "${myTime}");
}
Java DSL runner
@CitrusTest
public void templateTest() {
    variable("myTime", "citrus:currentDate()");

    applyTemplate(template -> template.name("doCreateVariables"));

    applyTemplate(template -> template.name("doTraceVariables")
                    .parameter("time", "${myTime}"));
}

There is an open question when dealing with templates that are defined somewhere else outside the test case. How to handle variables? A templates may use different variable names then the test and vice versa. No doubt the template will fail as soon as special variables with respective values are not present. Unknown variables cause the template and the whole test to fail with errors.

So a first approach would be to harmonize variable usage across templates and test cases, so that templates and test cases do use the same variable naming. But this approach might lead to high calibration effort. Therefore templates support parameters to solve this problem. When a template is called the calling actor is able to set some parameters. Let us discuss an example for this issue.

The template "doDateConversion" in the next sample uses the variable ${date}. The calling test case can set this variable as a parameter without actually declaring the variable in the test itself:

<call-template name="doDateConversion">
    <parameter name="date" value="${sampleDate}">
</call-template>

The variable sampleDate is already present in the test case and gets translated into the date parameter. Following from that the template works fine although test and template do work on different variable namings.

With template parameters you are able to solve the calibration effort when working with templates and variables. It is always a good idea to check the used variables/parameters inside a template when calling it. There might be a variable that is not declared yet inside your test. So you need to define this value as a parameter.

Template parameters may contain more complex values like XML fragments. The call-template action offers following CDATA variation for defining complex parameter values:

<call-template name="printXMLPayload">
    <parameter name="payload">
      <value>
        <![CDATA[
          <HelloRequest xmlns="http://www.consol.de/schemas/samples/sayHello.xsd">
            <Text>Hello South ${var}</Text>
          </HelloRequest>
        ]]>
      </value>
    </parameter>
</call-template>
When a template works on variable values and parameters changes to these variables will automatically affect the variables in the whole test. So if you change a variable’s value inside a template and the variable is defined inside the test case the changes will affect the variable in a global context. We have to be careful with this when executing a template several times in a test, especially in combination with parallel containers (see containers-parallel).
<parallel>
    <call-template name="print">
        <parameter name="param1" value="1"/>
        <parameter name="param2" value="Hello Europe"/>
    </call-template>
    <call-template name="print">
        <parameter name="param1" value="2"/>
        <parameter name="param2" value="Hello Asia"/>
    </call-template>
    <call-template name="print">
        <parameter name="param1" value="3"/>
        <parameter name="param2" value="Hello Africa"/>
    </call-template>
</parallel>

In the listing above a template print is called several times in a parallel container. The parameter values will be handled in a global context, so it is quite likely to happen that the template instances influence each other during execution. We might get such print messages:

2. Hello Europe
2. Hello Africa
3. Hello Africa

Index parameters do not fit and the message 'Hello Asia' is completely gone. This is because templates overwrite parameters to each other as they are executed in parallel at the same time. To avoid this behavior we need to tell the template that it should handle parameters as well as variables in a local context. This will enforce that each template instance is working on a dedicated local context. See the global-context attribute that is set to false in this example:

<template name="print" global-context="false">
    <echo>
        <message>${param1}.${param2}</message>
    </echo>
</template>

After that template instances won’t influence each other anymore. But notice that variable changes inside the template then do not affect the test case neither.

15. Test behaviors

Test behaviors combine action sequences to a logical unit. The behavior defines a set of test actions that can be applied to a Java DSL test case. Following from that you can say that behaviors are reusable test action templates. The maintenance is much more effective when you reuse basic test actions in many test cases.

The behavior is a separate Java DSL class with a single apply method that configures the test actions. Have a look at this first example:

Java DSL
public class FooBehavior extends AbstractTestBehavior {
    public void apply() {
        variable("foo", "test");

        echo("fooBehavior");
    }
}

public class BarBehavior extends AbstractTestBehavior {
    public void apply() {
        variable("bar", "test");

        echo("barBehavior");
    }
}

As you can see the behavior class is able to use the Citrus Java DSL as usual. Each behavior is able to define test variables and actions. In a test case you can apply the behaviors as follows:

Java DSL
@CitrusTest
public void behaviorTest() {
    variable("myTime", "citrus:currentDate()");

    FooBehavior fooBehavior = new FooBehavior();
    applyBehavior(fooBehavior);

    applyBehavior(new BarBehavior());

    applyBehavior(fooBehavior);
}

When dealing with behaviors test actions are defined somewhere outside the test case. How do we handle test variables? A behavior may use different variable names then the test and vice versa. No doubt the behavior will fail as soon as special variables with respective values are not present. Unknown variables cause the behavior and the whole test to fail with errors.

So a good approach would be to harmonize variable usage across behaviors and test cases, so that templates and test cases do use the same variable naming. The behavior automatically knows all variables in the test case. And all test variables created inside the behavior are visible to the test case after applying.

When a behavior changes variables this will automatically affect the variables in the whole test. So if you change a variable’s value inside a behavior and the variable is defined inside the test case the changes will affect the variable in a global test context. This means we have to be careful when executing a behavior several times in a test, especially in combination with parallel containers (see containers-parallel).

15.1. Behavior types

The test case in Java is able to follow either designer or runner strategies. This means we also have two different behavior types for designer and runner respectively. The behaviors are located in separate packages

  • com.consol.citrus.dsl.design.AbstractTestBehavior

  • com.consol.citrus.dsl.runner.AbstractTestBehavior

Decide which base behavior you want to extend from according to your test case nature.

16. Containers

Similar to templates a container element holds one to many test actions. In contrast to the template the container appears directly inside the test case action chain, meaning that the container is not referenced by more than one test case.

Containers execute the embedded test actions in specific logic. This can be an execution in iteration for instance. Combine different containers with each other and you will be able to generate very powerful hierarchical structures in order to create a complex execution logic. In the following sections some predefined containers are described.

16.1. Sequential

The sequential container executes the embedded test actions in strict sequence. Readers now might search for the difference to the normal action chain that is specified inside the test case. The actual power of sequential containers does show only in combination with other containers like iterations and parallels. We will see this later when handling these containers.

For now the sequential container seems not very sensational - one might say boring - because it simply groups a pair of test actions to sequential execution.

XML DSL
<testcase name="sequentialTest">
    <actions>
        <sequential>
            <trace-time/>
            <sleep/>
            <echo>
                <message>Hallo TestFramework</message>
            </echo>
            <trace-time/>
        </sequential>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void sequentialTest() {
    sequential()
        .actions(
            stopTime(),
            sleep(1.0),
            echo("Hello Citrus"),
            stopTime()
        );
}

16.2. Conditional

Now we deal with conditional executions of test actions. Nested actions inside a conditional container are executed only in case a boolean expression evaluates to true. Otherwise the container execution is not performed at all.

See some example to find out how it works with the conditional expression string.

XML DSL
<testcase name="conditionalTest">
    <variables>
      <variable name="index" value="5"/>
      <variable name="shouldSleep" value="true"/>
    </variables>

    <actions>
        <conditional expression="${index} = 5">
            <sleep seconds="10"/>
        </conditional>

        <conditional expression="${shouldSleep}">
            <sleep seconds="10"/>
        </conditional>

        <conditional expression="@assertThat('${shouldSleep}', 'anyOf(is(true), isEmptyString())')@">
            <sleep seconds="10"/>
        </conditional>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void conditionalTest() {
    variable("index", 5);
    variable("shouldSleep", true);

    conditional().when("${index} = 5"))
        .actions(
            sleep(10000L)
        );

    conditional().when("${shouldSleep}"))
        .actions(
            sleep(10000L)
        );

    conditional().when("${shouldSleep}", anyOf(is("true"), isEmptyString()))
        .actions(
            sleep(10000L)
        );
}

The nested sleep action is executed in case the variable ${index} is equal to the value '5'. This conditional execution of test actions is useful when dealing with different test environments such as different operating systems for instance. The conditional container also supports expressions that evaluate to the character sequence "true" or "false" as shown in the ${shouldSleep} example.

The last conditional container in the example above makes use of Hamcrest matchers. The matcher evaluates to true of false and based on that the container actions are executed or skipped. The Hamcrest matchers are very powerful when it comes to evaluation of multiple conditions at a time.

16.3. Parallel

Parallel containers execute the embedded test actions concurrent to each other. Every action in this container will be executed in a separate Java Thread. Following example should clarify the usage:

XML DSL
<testcase name="parallelTest">
    <actions>
        <parallel>
            <sleep/>

            <sequential>
                <sleep/>
                <echo>
                    <message>1</message>
                </echo>
            </sequential>

            <echo>
                <message>2</message>
            </echo>

            <echo>
                <message>3</message>
            </echo>

            <iterate condition="i lt= 5"
                        index="i">
                <echo>
                    <message>10</message>
                </echo>
            </iterate>
        </parallel>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void paralletTest() {
    parallel().actions(
        sleep(),
        sequential().actions(
            sleep(),
            echo("1")
        ),
        echo("2"),
        echo("3"),
        iterate().condition("i lt= 5").index("i"))
            .actions(
                echo("10")
            )
    );
}

So the normal test action processing would be to execute one action after another. As the first action is a sleep of five seconds, the whole test processing would stop and wait for 5 seconds. Things are different inside the parallel container. Here the descending test actions will not wait but execute at the same time.

Note that containers can easily wrap other containers. The example shows a simple combination of sequential and parallel containers that will archive a complex execution logic. Actions inside the sequential container will execute one after another. But actions in parallel will be executed at the same time.

16.4. Iterate

Iterations are very powerful elements when describing complex logic. The container executes the embedded actions several times. The container will continue with looping as long as the defined breaking condition string evaluates to true . In case the condition evaluates to false the iteration will break an finish execution.

XML DSL
<testcase name="iterateTest">
    <actions>
        <iterate index="i" condition="i lt 5">
            <echo>
                <message>index is: ${i}</message>
            </echo>
        </iterate>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void iterateTest() {
    iterate().condition("i lt 5").index("i"))
        .actions(
            echo("index is: ${i}")
        );
}

The attribute "index" automatically defines a new variable that holds the actual loop index starting at "1". This index variable is available as a normal variable inside the iterate container. Therefore it is possible to print out the actual loop index in the echo action as shown in the above example.

The condition string is mandatory and describes the actual end of the loop. In iterate containers the loop will break in case the condition evaluates to false .

The condition string can be any Boolean expression and supports several operators:

lt

lower than

lt=

lower than equals

gt

greater than

gt=

greater than equals

=

equals

and

logical combining of two Boolean values

or

logical combining of two Boolean values

()

brackets

It is very important to notice that the condition is evaluated before the very first iteration takes place. The loop therefore can be executed 0-n times according to the condition value.

Now the boolean expression evaluation as described above is limited to very basic operation such as lower than, greater than and so on. We also can use Hamcrest matchers in conditions that are way more powerful than that.

XML DSL
<testcase name="iterateTest">
    <actions>
        <iterate index="i" condition="@assertThat(lessThan(5))@">
            <echo>
                <message>index is: ${i}</message>
            </echo>
        </iterate>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void iterateTest() {
    iterate().condition(lessThan(5)).index("i"))
        .actions(
            echo("index is: ${i}")
        );
}

In the example above we use Hamcrest matchers as condition. You can combine Hamcrest matchers and create very powerful condition evaluations here.

16.5. Repeat until true

Quite similar to the previously described iterate container this repeating container will execute its actions in a loop according to an ending condition. The condition describes a Boolean expression using the operators as described in the previous chapter.

The loop continues its work until the provided condition evaluates to true . It is very important to notice that the repeat loop will execute the actions before evaluating the condition. This means the actions get executed n-1 times.
XML DSL
<testcase name="iterateTest">
    <actions>
        <repeat-until-true index="i" condition="(i = 3) or (i = 5)">
            <echo>
                <message>index is: ${i}</message>
            </echo>
        </repeat-until-true>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void repeatTest() {
    repeat().until("(i gt 5) or (i = 3)").index("i"))
        .actions(
            echo("index is: ${i}")
        );
}

As you can see the repeat container is only executed when the iterating condition expression evaluates to false . By the time the condition is true execution is discontinued. You can use basic logical operators such as and, or and so on.

A more powerful way is given by Hamcrest matchers that are directly supported in condition expressions.

XML DSL
<testcase name="iterateTest">
    <actions>
        <repeat-until-true index="i" condition="@assertThat(anyOf(is(3), is(5))@">
            <echo>
                <message>index is: ${i}</message>
            </echo>
        </repeat-until-true>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void repeatTest() {
    repeat().until(anyOf(is(3), is(5)).index("i"))
        .actions(
            echo("index is: ${i}")
        );
}

The Hamcrest matcher usage simplifies the reading a lot. And it empowers you to combine more complex condition expressions. So I personally prefer this syntax.

16.6. Repeat on error until true

The next looping container is called repeat-on-error-until-true. This container repeats a group of actions in case one embedded action failed with error. In case of an error inside the container the loop will try to execute all embedded actions again in order to seek for overall success. The execution continues until all embedded actions were processed successfully or the ending condition evaluates to true and the error-loop will lead to final failure.

XML DSL
<testcase name="iterateTest">
    <actions>
        <repeat-onerror-until-true index="i" condition="i = 5">
            <echo>
                <message>index is: ${i}</message>
            </echo>
            <fail/>
        </repeat-onerror-until-true>
    </actions>
</testcase>
Java DSL designer
@CitrusTest
public void repeatOnErrorTest() {
    repeatOnError(
        echo("index is: ${i}"),
        fail("Force loop to fail!")
    ).until("i = 5").index("i");
}
Java DSL runner
@CitrusTest
public void repeatOnErrorTest() {
    repeatOnError().until("i = 5").index("i"))
        .actions(
            echo("index is: ${i}"),
            fail("Force loop to fail!")
        );
}

In the code example the error-loop continues four times as the <fail> action definitely fails the test. During the fifth iteration The condition "i=5" evaluates to true and the loop breaks its processing leading to a final failure as the test actions were not successful.

The overall success of the test case depends on the error situation inside the repeat-onerror-until-true container. In case the loop breaks because of failing actions and the loop will discontinue its work the whole test case is failing too. The error loop processing is successful in case all embedded actions were not raising any errors during an iteration.

The repeat-on-error container also offers an automatic sleep mechanism. This auto-sleep property will force the container to wait a given amount of time before executing the next iteration. We used this mechanism a lot when validating database entries. Let’s say we want to check the existence of an order entry in the database. Unfortunately the system under test is not very well performing and may need some time to store the new order. This amount of time is not predictable, especially when dealing with different hardware on our test environments (local testing vs. server testing). Following from that our test case may fail unpredictable only because of runtime conditions.

We can avoid unstable test cases that are based on these runtime conditions with the auto-sleep functionality.

XML DSL
<repeat-onerror-until-true auto-sleep="1000" condition="i = 5" index="i">
    <echo>
        <sql datasource="testDataSource">
            <statement>
              SELECT COUNT(1) AS CNT_ORDERS
              FROM ORDERS
              WHERE CUSTOMER_ID='${customerId}'
            </statement>
            <validate column="CNT_ORDERS" value="1"/>
        </sql>
    </echo>
</repeat-onerror-until-true>
Java DSL
@CitrusTest
public void repeatOnErrorTest() {
    repeatOnError().until("i = 5").index("i").autoSleep(1000))
        .actions(
            query(action -> action.dataSource(testDataSource)
                .statement("SELECT COUNT(1) AS CNT_ORDERS FROM ORDERS WHERE CUSTOMER_ID='${customerId}'")
                .validate("CNT_ORDERS", "1"))
        );
}

We surrounded the database check with a repeat-onerror container having the auto-sleep property set to 1000 milliseconds. The repeat container will try to check the database up to five times with an automatic sleep of 1 second before every iteration. This gives the system under test up to five seconds time to store the new entry to the database. The test case is very stable and just fits to the hardware environment. On slow test environments the test may need several iterations to successfully read the database entry. On very fast environments the test may succeed right on the first try.

We changed auto sleep time from seconds to milliseconds with Citrus 2.0 release. So if you are coming from previous Citrus versions be sure to now use proper millisecond values.

So fast environments are not slowed down by static sleep operations and slower environments are still able to execute this test case with high stability.

16.7. Timer

Timers are very useful containers when you wish to execute a collection of test actions several times at regular intervals. The timer component generates an event which in turn triggers the execution of the nested test actions associated with timer. This can be useful in a number of test scenarios for example when Citrus needs to simulate a heart beat or if you are debugging a test and you wist to query the contents of the database, to mention just a few. The following code sample should demonstrate the power and flexibility of timers:

XML DSL
<testcase name="timerTest">
      <actions>
        <timer id="forkedTimer" interval="100" fork="true">
          <echo>
            <message>I'm going to run in the background and let some other test actions run (nested action run ${forkedTimer-index} times)</message>
          </echo>
          <sleep milliseconds="50" />
        </timer>

        <timer repeatCount="3" interval="100" delay="50">
          <sleep milliseconds="50" />
          <echo>
            <message>I'm going to repeat this message 3 times before the next test actions are executed</message>
          </echo>
        </timer>

        <echo>
          <message>Test almost complete. Make sure all timers running in the background are stopped</message>
        </echo>
      </actions>
      <finally>
        <stop-timer timerId="forkedTimer" />
      </finally>
    </testcase>
Java DSL
@CitrusTest
public void timerTest() {

    timer()
        .timerId("forkedTimer")
        .interval(100L)
        .fork(true)
        .actions(
            echo("I'm going to run in the background and let some other test actions run (nested action run ${forkedTimer-index} times)"),
            sleep(50L)
        );

    timer()
        .repeatCount(3)
        .interval(100L)
        .delay(50L)
        .actions(
            sleep(50L),
            echo("I'm going to repeat this message 3 times before the next test actions are executed")
        );

    echo("Test almost complete. Make sure all timers running in the background are stopped");

    doFinally().actions(
        stopTimer("forkedTimer")
    );
}

In the above example the first timer (timerId = forkedTimer) is started in the background. By default timers are run in the current thread of execution but to start it in the background just use "fork=true". Every 100 milliseconds this timer emits an event which will result in the nested actions being executed. The nested 'echo' action outputs the number of times this timer has already been executed. It does this with the help of an 'index' variable, in this example ${forkedTimer-index}, which is named according to the timer id with the suffix '-index'. No limit is set on the number of times this timer should run so it will keep on running until either a nested test action fails or it is instructed to stop (more on this below).

The second timer is configured to run 3 times with a delay of 100 milliseconds between each iteration. Using the attribute 'delay' we can get the timer pause for 50 milliseconds before running the nested actions for the first time. The timer is configured to run in the current thread of execution so the last test action, the 'echo', has to wait for this timer to complete before it is executed.

So how do we tell the forked timer to stop running? If we forget to do this the timer will just execute indefinitely. To help us out here we can use the 'stop-timer' action. By adding this to the finally block we ensure that the timer will be stopped, even if some nested test action fails. We could have easily added it as a nested test action, to the forkedTimer for example, but if some other test action failed before the stop-timer was called, the timer would never stop.

You can also configure timers to run in the background using the 'parallel' container, rather than setting the attribute 'fork' to true. Using parallel allows more fine-grained control of the test and has the added advantage that all errors generated from a nester timer action are visible to the test executer. If an error occurs within the timer then the test status is set to failed. Using fork=true an error causes the timer to stop executing, but the test status is not influenced by this error.

16.8. Async

Now we deal with parallel execution of test actions. Nested actions inside a async container are executed in a separate thread. This has the effect that the test execution is not blocked until the nested actions have performed. The test immediately continues with the next test actions in place the will be executed in parallel to those actions in the async container.

This mechanism comes in handy when a test action should be forked to the rest of the test. In send operations we were able to do this before with the fork="true" option set. Now we can also use the async test action container with all kind of test actions nested.

See some example to find out how it works.

XML DSL
<testcase name="asyncTest">
    <actions>
        <async>
            <actions>
                <send endpoint="fooEndpoint">
                    <message>...</message>
                </send>
                <receive endpoint="fooEndpoint">
                    <message>...</message>
                </echo>
            </actions>
        </async>

        <echo>
          <message>Continue with test</message>
        </echo>
    </actions>
</testcase>
Java DSL
@CitrusTest
public void asyncTest() {
    async().actions(
            send(fooEndpoint)
                .message(fooRequest()),
            receive(fooEndpoint)
                .message(fooResponse())
        );

    echo("Continue with test");
}

The nested send and receive actions get executed in parallel to the other test actions in that test case. So the test will not wait for these actions to finish before executing next actions. Of course possible errors inside the async container will also cause the whole test case to fail. And the test will definitely wait for all async actions to be finished before finishing the whole test case. This safely lets us execute test actions in parallel to each other.

The async container also supports success and error callback actions. This is an experimental feature that is only available for XML test cases up to now.

XML DSL
<testcase name="asyncTest">
    <actions>
        <async>
            <actions>
                <send endpoint="fooEndpoint">
                    <message>...</message>
                </send>
                <receive endpoint="fooEndpoint">
                    <message>...</message>
                </echo>
                <success>
                    <echo><message>Success!</message></echo>
                </success>
                <error>
                    <echo><message>Failed!</message></echo>
                </error>
            </actions>
        </async>

        <echo>
          <message>Continue with test</message>
        </echo>
    </actions>
</testcase>

So you can add test actions that get executed based on the async test action outcome success or error.

16.9. Custom containers

In case you have a custom action container implementation you might also want to use it in Java DSL. The action containers are handled with special care in the Java DSL because they have nested actions. So when you call a test action container in the Java DSL you always have something like this:

Java DSL
@CitrusTest
public void containerTest() {
    echo("This echo is outside of the action container");

    sequential()
        .actions(
            echo("Inside"),
            echo("Inside once more"),
            echo("And again: Inside!")
        );

    echo("This echo is outside of the action container");
}

Now the three nested actions are added to the action sequential container rather than to the test case itself although we are using the same action Java DSL methods as outside the container. This mechanism is only working because Citrus is handling test action containers with special care.

A custom test action container implementation could look like this:

public class ReverseActionContainer extends AbstractActionContainer {
    @Override
    public void doExecute(TestContext context) {
        for (int i = getActions().size(); i > 0; i--) {
            getActions().get(i-1).execute(context);
        }
    }
}

The container logic is very simple: The container executes the nested actions in reverse order. As already mentioned Citrus needs to take special care on all action containers when executing a Java DSL test. This is why you should not execute a custom test container implementation on your own.

@CitrusTest
public void containerTest() {
    ReverseActionContainer reverseContainer = new ReverseActionContainer();
    reverseContainer.addTestAction(new EchoAction().setMessage("Foo"));
    reverseContainer.addTestAction(new EchoAction().setMessage("Bar"));
    run(reverseContainer);
}

The above custom container execution is going to fail with internal error as the Citrus Java DSL was not able to recognise the action container as it should be. Also the EchoAction instance creation is not very comfortable. Instead you can use a special container Java DSL syntax also with your custom container implementation:

@CitrusTest
public void containerTest() {
    container(new ReverseActionContainer()).actions(
        echo("Foo"),
        echo("Bar")
    );
}

The custom container implementation now works fine with the automatically nested echo actions. And we are able to use the usual Java DSL syntactic sugar for test actions like echo .

In a next step we add a custom superclass for all our test classes which provides a helper method for the custom container implementation in order to have a even more comfortable syntax.

Java DSL
public class CustomCitrusBaseTest extends TestNGCitrusTestDesigner {

    public AbstractTestContainerBuilder<ReverseActionContainer> reverse() {
        return container(new ReverseActionContainer());
    }
}

Now all subclasses can use the new reverse method for calling the custom container implementation.

@CitrusTest
public void containerTest() {
    reverse().actions(
        echo("Foo"),
        echo("Bar")
    );
}

Nice! This is how we should integrate customized test action containers to the Citrus Java DSL.

17. JMS support

Citrus provides support for sending and receiving JMS messages. We have to separate between synchronous and asynchronous communication. So in this chapter we explain how to setup JMS message endpoints for synchronous and asynchronous outbound and inbound communication

The JMS components in Citrus are kept in a separate Maven module. If not already done so you have to include the module as Maven dependency to your project
<dependency>
  <groupId>com.consol.citrus</groupId>
  <artifactId>citrus-jms</artifactId>
  <version>2.7.4</version>
</dependency>

Citrus provides a "citrus-jms" configuration namespace and schema definition for JMS related components and features. Include this namespace into your Spring configuration in order to use the Citrus JMS configuration elements. The namespace URI and schema location are added to the Spring configuration XML file as follows.

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:citrus-jms="http://www.citrusframework.org/schema/jms/config"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.citrusframework.org/schema/jms/config
       http://www.citrusframework.org/schema/jms/config/citrus-jms-config.xsd">

    [...]

</beans>

After that you are able to use customized Citrus XML elements in order to define the Spring beans.

17.1. JMS endpoints

By default Citrus JMS endpoints are asynchronous. So let us first of all deal with asynchronous messaging which means that we will not wait for any response message after sending or receiving a message.

The test case itself should not know about JMS transport details like queue names or connection credentials. This information is stored in the endpoint component configuration that lives in the basic Spring configuration file in Citrus. So let us have a look at a simple JMS message endpoint configuration in Citrus.

<citrus-jms:endpoint id="helloServiceQueueEndpoint"
          destination-name="Citrus.HelloService.Request.Queue"
          timeout="10000"/>

The endpoint component receives an unique id and a JMS destination name. This can be a queue or topic destination. We will deal with JMS topics later on. For now the timeout setting completes our first JMS endpoint component definition.

The endpoint needs a JMS connection factory for connecting to a JMS message broker. The connection factory is also added as Spring bean to the Citrus Spring application context.

<bean id="connectionFactory"
         class="org.apache.activemq.ActiveMQConnectionFactory">
    <property name="brokerURL" value="tcp://localhost:61616" />
</bean>

The JMS connection factory receives the JMS message broker URL and is able to hold many other connection specific options. In this example we use the Apache ActiveMQ connection factory implementation as we want to use the ActiveMQ message broker. Citrus works by default with a bean id connectionFactory . All Citrus JMS component will automatically recognize this connection factory.

Spring makes it very easy to connect to other JMS broker implementations too (e.g. Apache ActiveMQ, TIBCO Enterprise Messaging Service, IBM Websphere MQ). Just add the required connection factory implementation as connectionFactory bean.
All of the Citrus JMS endpoint components will automatically look for a bean named connectionFactory by default. You can use the connection-factory endpoint attribute in order to use another connection factory instance with different bean names.
<citrus-jms:endpoint id="helloServiceQueueEndpoint"
      destination-name="Citrus.HelloService.Request.Queue"
      connection-factory="myConnectionFacotry"/>

As an alternative to that you may want to use a special Spring jms template implementation as custom bean in your endpoint.

<citrus-jms:endpoint id="helloServiceQueueEndpoint"
              destination-name="Citrus.HelloService.Request.Queue"
              jms-template="myJmsTemplate"/>

The endpoint is now ready to be used inside a test case. Inside a test case you can send or receive messages using this endpoint. The test actions can reference the JMS endpoint using its identifier. When sending a message the message endpoint creates a JMS message producer and will simply publish the message to the defined JMS destination. As the communication is asynchronous by default producer does not wait for a synchronous response.

When receiving a messages with this endpoint the endpoint creates a JMS consumer on the JMS destination. The endpoint then acts as a message driven listener. This means that the message consumer connects to the given destination and waits for messages to arrive.

Besides the destination-name attribute you can also provide a reference to a destination implementation.
<citrus-jms:endpoint id="helloServiceQueueEndpoint"
                                destination="helloServiceQueue"/>

<amq:queue id="helloServiceQueue" physicalName="Citrus.HelloService.Request.Queue"/>

The destination attribute references to a JMS destination object in the Spring application context. In the example above we used the ActiveMQ queue destination component. The destination reference can also refer to a JNDI lookup for instance.

17.2. JMS synchronous endpoints

When using synchronous message endpoints Citrus will manage a reply destination for receiving a synchronous response message on the reply destination. The following figure illustrates that we now have two destinations in our communication scenario.

figure_006.jpg

The synchronous message endpoint component is similar to the asynchronous brother that we have discussed before. The only difference is that the endpoint will automatically manage a reply destination behind the scenes. By default Citrus uses temporary reply destinations that get automatically deleted after the communication handshake is done. Again we need to use a JMS connection factory in the Spring XML configuration as the component need to connect to a JMS message broker.

<citrus-jms:sync-endpoint id="helloServiceSyncEndpoint"
          destination-name="Citrus.HelloService.InOut.Queue"
          timeout="10000"/>

The synchronous component defines a target destination which again is either a queue or topic destination. If nothing else is defined the endpoint will create temporary reply destinations on its own. When the endpoint has sent a message it waits synchronously for the response message to arrive on the reply destination. You can receive this reply message in your test case by referencing this same endpoint in a receive test action. In case no reply message arrives in time a message timeout error is raised respectively.

See the following example test case which references the synchronous message endpoint in its send and receive test action in order to send out a message and wait for the synchronous response.

<testcase name="synchronousMessagingTest">
    <actions>
        <send endpoint="helloServiceSyncEndpoint">
            <message>
                <data>
                  [...]
                </data>
            </message>
        </send>

        <receive endpoint="helloServiceSyncEndpoint">
            <message>
                <data>
                  [...]
                </data>
            </message>
        </receive>
    </actions>
</testcase>

We initiated the synchronous communication by sending a message on the synchronous endpoint. The second step then receives the synchronous message on the temporary reply destination that was automatically created for us.

If you rather want to define a static reply destination you can do so, too. The static reply destination is not deleted after communication handshake. You may need to work with message selectors then in order to pick the right response message that belongs to a specific communication handshake. You can define a static reply destination on the synchronous endpoint component as follows.

<citrus-jms:sync-endpoint id="helloServiceSyncEndpoint"
          destination-name="Citrus.HelloService.InOut.Queue"
          reply-destination-name="Citrus.HelloService.Reply.Queue"
          timeout="10000"/>

Instead of using the reply-destination-name feel free to use the destination reference with reply-destination attribute. Again you can use a JNDI lookup then to reference a destination object.

Be aware of permissions that are mandatory for creating temporary destinations. Citrus tries to create temporary queues on the JMS message broker. Following from that the Citrus JMS user has to have the permission to do so. Be sure that the user has the sufficient rights when using temporary reply destinations.

Up to now we have sent a message and waited for a synchronous response in the next step. Now it is also possible to switch the directions of send and receive actions. Then we have the situation where Citrus receives a JMS message first and then Citrus is in charge of providing a proper synchronous response message to the initial sender.

figure_007.jpg

In this scenario the foreign message producer has stored a dynamic JMS reply queue destination to the JMS header. So Citrus has to send the reply message to this specific reply destination, which is dynamic of course. Fortunately the heavy lift is done with the JMS message endpoint and we do not have to change anything in our configuration. Again we just define a synchronous message endpoint in the application context.

<citrus-jms:sync-endpoint id="helloServiceSyncEndpoint"
      destination-name="Citrus.HelloService.InOut.Queue"
      timeout="10000"/>

Now the only thing that changes here is that we first receive a message in our test case on this endpoint. The second step is a send message action that references this same endpoint and we are done. Citrus automatically manages the reply destinations for us.

<testcase name="synchronousMessagingTest">
  <actions>
        <receive endpoint="helloServiceSyncEndpoint">
            <message>
                <data>
                  [...]
                </data>
            </message>
        </receive>

        <send endpoint="helloServiceSyncEndpoint">
            <message>
                <data>
                  [...]
                </data>
            </message>
        </send>
    </actions>
</testcase>

17.3. JMS topics

Up to now we have used JMS queue destinations on our endpoints. Citrus is also able to connect to JMS topic destinations. In contrary to JMS queues which represents the point-to-point communication JMS topics use publish-subscribe mechanism in order to spread messages over JMS. A JMS topic producer publishes messages to the topic, while the topic accepts multiple message subscriptions and delivers the message to all subscribers.

The Citrus JMS endpoints offer the attribute 'pub-sub-domain' . Once this attribute is set to true Citrus will use JMS topics instead of queue destinations. See the following example where the publish-subscribe attribute is set to true in JMS message endpoint components.

<citrus-jms:endpoint id="helloServiceQueueEndpoint"
            destination="helloServiceQueue"
            pub-sub-domain="true"/>

When using JMS topics you will be able to subscribe several test actions to the topic destination and receive a message multiple times as all subscribers will receive the message.

It is very important to keep in mind that Citrus does not deal with durable subscribers. This means that messages that were sent in advance to the message subscription are not delivered to the message endpoint. So racing conditions may cause problems when using JMS topic endpoints in Citrus. Be sure to let Citrus subscribe to the topic before messages are sent to it. Otherwise you may loose some messages that were sent in advance to the subscription.

17.4. JMS message headers

The JMS specification defines a set of special message header entries that can go into your JMS message. These JMS headers are stored differently in a JMS message header than other custom header entries do. Therefore these special header values should be set in a special syntax that we discuss in the next paragraphs.

<header>
    <element name="citrus_jms_correlationId" value="${correlationId}"/>
    <element name="citrus_jms_messageId" value="${messageId}"/>
    <element name="citrus_jms_redelivered" value="${redelivered}"/>
    <element name="citrus_jms_timestamp" value="${timestamp}"/>
</header>

As you see all JMS specific message headers use the citrus_jms_ prefix. This prefix comes from Spring Integration message header mappers that take care of setting those headers in the JMS message header properly.

Typing of message header entries may also be of interest in order to meet the JMS standards of typed message headers. For instance the following message header is of type double and is therefore transferred via JMS as a double value.

<header>
    <element name="amount" value="19.75" type="double"/>
</header>

17.5. Dynamic destination names

Usually you set the target destination as property on the JMS endpoint component. In some cases it might be useful to set the target destination in a more dynamic way during the test run. You can do this by adding a special message header named citrus_jms_destination_name. This header is automatically interpreted by the Citrus JMS endpoint and is set as the target destination before a message is sent.

<send endpoint="jmsEndpoint">
    <message>
        ...
    </message>
    <header>
        <element name="citrus_jms_destination_name" value="dynamic.destination.name"/>
    </header>
</send>

This action above will send the message to the destination "dynamic.destination.name" no matter what default destination is set on the referenced endpoint component named jmsEndpoint. The dynamic destination name setting also supports test variables so you can use variables and functions in the destination name, too.

Another possibility for dynamic JMS destinations is given with the dynamic endpoints.

17.6. SOAP over JMS

When sending SOAP messages you have to deal with proper envelope, body and header construction. In Citrus you can add a special message converter that performs the heavy lift for you. Just add the message converter to the JMS endpoint as shown in the next program listing:

<citrus-jms:endpoint id="helloServiceSoapJmsEndpoint"
        destination-name="Citrus.HelloService.Request.Queue"
        message-converter="soapJmsMessageConverter"/>

<bean id="soapJmsMessageConverter" class="com.consol.citrus.jms.message.SoapJmsMessageConverter"/>

With this message converter you can skip the SOAP envelope completely in your test case. You just deal with the message body payload and the header entries. The rest is done by the message converter. So you get proper SOAP messages on the producer and consumer side.

18. HTTP REST support

REST APIs have gained more and more significance regarding client-server interfaces. The REST client is nothing but a HTTP client sending HTTP requests usually in JSON data format to a HTTP server. As HTTP is a synchronous protocol by nature the client receives the server response synchronously. Citrus is able to connect with HTTP services and test REST APIs on both client and server side with a powerful JSON message data support. In the next sections you will learn how to invoke HTTP services as a client and how to handle REST HTTP requests in a test case. We deal with setting up a HTTP server in order to accept client requests and provide proper HTTP responses with GET, PUT, DELETE or POST request method.

The http components in Citrus are kept in a separate Maven module. So you should add the module as Maven dependency to your project accordingly.
<dependency>
  <groupId>com.consol.citrus</groupId>
  <artifactId>citrus-http</artifactId>
  <version>2.7.4</version>
</dependency>

As Citrus provides a customized HTTP configuration schema for the Spring application context configuration files we have to add name to the top level beans element. Simply include the http-config namespace in the configuration XML files as follows.

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:citrus="http://www.citrusframework.org/schema/config"
   xmlns:citrus-http="http://www.citrusframework.org/schema/http/config"
   xsi:schemaLocation="
   http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.citrusframework.org/schema/config
   http://www.citrusframework.org/schema/config/citrus-config.xsd
   http://www.citrusframework.org/schema/http/config
   http://www.citrusframework.org/schema/http/config/citrus-http-config.xsd">

    [...]

</beans>

Now we are ready to use the customized Citrus HTTP configuration elements with the citrus-http namespace prefix.

18.1. HTTP REST client

On the client side we have a simple HTTP message client component connecting to the server. The request-url attribute defines the HTTP server endpoint URL to connect to. As usual you can reference this client in your test case in order to send and receive messages. Citrus as client waits for the response message from server. After that the response message goes through the validation process as usual. Let us see how a Citrus HTTP client component looks like:

<citrus-http:client id="helloHttpClient"
          request-url="http://localhost:8080/hello"
          request-method="GET"
          content-type="application/xml"
          charset="UTF-8"
          timeout="60000"/>

The request-method defines the HTTP method to use. In addition to that we can specify the content-type of the request we are about to send. The charset is also added to the content-type header. In case you do not want to set the charset at all please specify an empty string as the default value is UTF-8. The client builds the HTTP request and sends it to the HTTP server. While the client is waiting for the synchronous HTTP response to arrive we are able to poll several times for the response message in our test case. As usual aou can use the same client endpoint in your test case to send and receive messages synchronously. In case the reply message comes in too late according to the timeout settings a respective timeout error is raised.

Http defines several request methods that a client can use to access Http server resources. In the example client above we are using GET as default request method. Of course you can overwrite this setting in a test case action by setting the HTTP request method inside the sending test action. The Http client component can be used as normal endpoint in a sending test action. Use something like this in your test:

XML DSL
<send endpoint="helloHttpClient">
    <message>
        <payload>
            <TestMessage>
                <Text>Hello HttpServer</Text>
            </TestMessage>
        </payload>
    </message>
    <header>
        <element name="citrus_http_method" value="POST"/>
    </header>
</send>
Citrus uses the Spring REST template mechanism for sending out HTTP requests. This means you have great customizing opportunities with a special REST template configuration. You can think of basic HTTP authentication, read timeouts and special message factory implementations. Just use the custom REST template attribute in client configuration like this:
<citrus-http:client id="helloHttpClient"
                   request-url="http://localhost:8080/hello"
                   request-method="GET"
                   content-type="text/plain"
                   rest-template="customizedRestTemplate"/>

<!-- Customized rest template -->
<bean name="customizedRestTemplate" class="org.springframework.web.client.RestTemplate">
  <property name="messageConverters">
    <util:list id="converter">
      <bean class="org.springframework.http.converter.StringHttpMessageConverter">
        <property name="supportedMediaTypes">
          <util:list id="types">
            <value>text/plain</value>
          </util:list>
        </property>
      </bean>
    </util:list>
  </property>
  <property name="errorHandler">
    <!-- Custom error handler -->
  </property>
  <property name="requestFactory">
    <bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
      <property name="readTimeout" value="9000" />
    </bean>
  </property>
</bean>

Up to now we have used a normal send test action to send Http requests as a client. This is completely valid strategy as the Citrus Http client is a normal endpoint. But we might want to set some more Http REST specific properties and settings. In order to simplify the Http usage in a test case we can use a special test action implementation. The Citrus Http specific actions are located in a separate XML namespace. So wen need to add this namespace to our test case XML first.

<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:http="http://www.citrusframework.org/schema/http/testcase"
        xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.citrusframework.org/schema/http/testcase
        http://www.citrusframework.org/schema/http/testcase/citrus-http-testcase.xsd">

      [...]

    </beans>

The test case is now ready to use the specific Http test actions by using the prefix http: .

XML DSL
<http:send-request client="httpClient">
  <http:POST path="/customer">
    <http:headers content-type="application/xml" accept="application/xml, */*">
      <http:header name="X-CustomHeaderId" value="${custom_header_id}"/>
    </http:headers>
    <http:body>
      <http:data>
        <![CDATA[
          <customer>
            <id>citrus:randomNumber()</id>
            <name>testuser</name>
          </customer>
        ]]>
      </http:data>
    </http:body>
  </http:POST>
</http:send-request>

The action above uses several Http specific settings such as the request method POST as well as the content-type and accept headers. As usual the send action needs a target Http client endpoint component. We can specify a request path attribute that added as relative path to the base uri used on the client.

When using a GET request we can specify some request uri parameters.

XML DSL
<http:send-request client="httpClient">
  <http:GET path="/customer/${custom_header_id}">
    <http:params content-type="application/xml" accept="application/xml, */*">
      <http:param name="type" value="active"/>
    </http:params>
  </http:GET>
</http:send-request>

The send action above uses a GET request on the endpoint uri http://localhost:8080/customer/1234?type=active.

Of course when sending Http client requests we are also interested in receiving Http response messages. We want to validate the success response with Http status code.

XML DSL
<http:receive-response client="httpClient">
  <http:headers status="200" reason-phrase="OK" version="HTTP/1.1">
    <http:header name="X-CustomHeaderId" value="${custom_header_id}"/>
  </http:headers>
  <http:body>
    <http:data>
      <![CDATA[
          <customerResponse>
            <success>true</success>
          </customerResponse>
      ]]>
    </http:data>
  </http:body>
</http:receive-response>

The receive-response test action also uses a client component. We can expect response status code information such as status and reason-phrase . Of course Citrus will raise a validation exception in case Http status codes mismatch.

Up to now we have used XML DSL test cases. The Java DSL in Citrus also works with specific Http test actions. See following example and find out how this works:

XML DSL
@CitrusTest
public void httpActionTest() {
    http().client("httpClient")
          .send()
          .post("/customer")
          .payload("<customer>" +
                    "<id>citrus:randomNumber()</id>" +
                    "<name>testuser</name>" +
                  "</customer>")
          .header("X-CustomHeaderId", "${custom_header_id}")
          .contentType("text/xml")
          .accept("text/xml, */*");

    http().client("httpClient")
          .receive()
          .response(HttpStatus.OK)
          .payload("<customerResponse>" +
                    "<success>true</success>" +
                  "</customerResponse>")
          .header("X-CustomHeaderId", "${custom_header_id}")
          .version("HTTP/1.1");
}

There is one more setting on the client to be aware of. By default the client component will add the Accept http header and set its value to a list of all supported encodings on the host operating system. As this list can get very long you may want to not set this default accept header. The setting is done in the Spring RestTemplate:

<bean name="customizedRestTemplate" class="org.springframework.web.client.RestTemplate">
    <property name="messageConverters">
        <util:list id="converter">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="writeAcceptCharset" value="false"/>
            </bean>
        </util:list>
    </property>
</bean>

You would have add this custom RestTemplate configuration and set it to the client component with rest-template property. But fortunately the Citrus client component provides a separate setting default-accept-header which is a Boolean setting. By default it is set to true so the default accept header is automatically added to all requests. If you set this flag to false the header is not set:

<citrus-http:client id="helloHttpClient"
                   request-url="http://localhost:8080/hello"
                   request-method="GET"
                   content-type="text/plain"
                   default-accept-header="false"/>

Of course you can set the Accept header on each send operation in order to tell the server what kind of content types are supported in response messages.

Now we can send and receive messages as Http client with specific test actions. Now lets move on to the Http server.

18.2. HTTP client interceptors

The client component is able to add custom interceptors that participate in the request/response processing. The interceptors need to implement the common interface org.springframework.http.client.ClientHttpRequestInterceptor.

<citrus-http:client id="helloHttpClient"
                  request-url="http://localhost:8080/hello"
                  request-method="GET"
                  interceptors="clientInterceptors"/>

<util:list id="clientInterceptors">
  <bean class="com.consol.citrus.http.interceptor.LoggingClientInterceptor"/>
</util:list>

The sample above adds the Citrus logging client interceptor that logs requests and responses exchanged with that client component. You can add custom interceptor implementations here in order to participate in the request/response message processing.

18.3. HTTP REST server

The HTTP client was quite easy and straight forward. Receiving HTTP messages is a little bit more complicated because Citrus has to provide server functionality listening on a local port for client connections. Therefore Citrus offers an embedded HTTP server which is capable of handling incoming HTTP requests. Once a client connection is accepted the HTTP server must also provide a proper HTTP response to the client. In the next few lines you will see how to simulate server side HTTP REST service with Citrus.

<citrus-http:server id="helloHttpServer"
                port="8080"
                auto-start="true"
                resource-base="src/it/resources"/>

Citrus uses an embedded Jetty server that will automatically start when the Spring application context is loaded (auto-start="true"). The basic connector is listening on port 8080 for requests. Test cases can interact with this server instance via message channels by default. The server provides an inbound channel that holds incoming request messages. The test case can receive those requests from the channel with a normal receive test action. In a second step the test case can provide a synchronous response message as reply which will be automatically sent back to the HTTP client as response.

figure_008.jpg

The figure above shows the basic setup with inbound channel and reply channel. You as a tester should not worry about this to much. By default you as a tester just use the server as synchronous endpoint in your test case. This means that you simply receive a message from the server and send a response back.

<testcase name="httpServerTest">
    <actions>
        <receive endpoint="helloHttpServer">
            <message>
                <data>
                  [...]
                </data>
            </message>
        </receive>

        <send endpoint="helloHttpServer">
            <message>
                <data>
                  [...]
                </data>
            </message>
        </send>
    </actions>
</testcase>

As you can see we reference the server id in both receive and send actions. The Citrus server instance will automatically send the response back to the calling HTTP client. In most cases this is exactly what we want to do - send back a response message that is specified inside the test. The HTTP server component by default uses a channel endpoint adapter in order to forward all incoming requests to an in memory message channel. This is done completely behind the scenes. The Http server component provides some more customization possibilities when it comes to endpoint adapter implementations. This topic is discussed in a separate section endpoint-adapter. Up to now we keep it simple by synchronously receiving and sending messages in the test case.

The default channel endpoint adapter automatically creates an inbound message channel where incoming messages are stored to internally. So if you need to clean up a server that has already stored some incoming messages you can do this easily by purging the internal message channel. The message channel follows a naming convention {serverName}.inbound where {serverName} is the Spring bean name of the Citrus server endpoint component. If you purge this internal channel in a before test nature you are sure that obsolete messages on a server instance get purged before each test is executed.

So lets get back to our mission of providing response messages as server to connected clients. As you might know Http REST works with some characteristic properties when it comes to send and receive messages. For instance a client can send different request methods GET, POST, PUT, DELETE, HEAD and so on. The Citrus server may verify this method when receiving client requests. Therefore we have introduced special Http test actions for server communication. Have a look at a simple example:

<http:receive-request server="helloHttpServer">
  <http:POST path="/test">
    <http:headers content-type="application/xml" accept="application/xml, */*">
      <http:header name="X-CustomHeaderId" value="${custom_header_id}"/>
      <http:header name="Authorization" value="Basic c29tZVVzZXJuYW1lOnNvbWVQYXNzd29yZA=="/>
    </http:headers>
    <http:body>
    <http:data>
      <![CDATA[
        <testRequestMessage>
          <text>Hello HttpServer</text>
        </testRequestMessage>
      ]]>
    </http:data>
    </http:body>
  </http:POST>
  <http:extract>
    <http:header name="X-MessageId" variable="message_id"/>
  </http:extract>
</http:receive-request>

<http:send-response server="helloHttpServer">
  <http:headers status="200" reason-phrase="OK" version="HTTP/1.1">
    <http:header name="X-MessageId" value="${message_id}"/>
    <http:header name="X-CustomHeaderId" value="${custom_header_id}"/>
    <http:header name="Content-Type" value="application/xml"/>
  </http:headers>
  <http:body>
  <http:data>
    <![CDATA[
      <testResponseMessage>
        <text>Hello Citrus</text>
      </testResponseMessage>
    ]]>
  </http:data>
  </http:body>
</http:send-response>

We receive a client request and validate that the request method is POST on request path /test . Now we can validate special message headers such as content-type . In addition to that we can check custom headers and basic authorization headers. As usual the optional message body is compared to an expected message template. The custom X-MessageId header is saved to a test variable message_id for later usage in the response.

The response message defines Http typical entities such as status and reason-phrase . Here the tester can simulate 404 NOT_FOUND errors or similar other status codes that get send back to the client. In our example everything is OK and we send back a response body and some custom header entries.

That is basically how Citrus simulates Http server operations. We receive the client request and validate the request properties. Then we send back a response with a Http status code.

As usual all these Http specific actions are also available in Java DSL.

@CitrusTest
public void httpServerActionTest() {
    http().server("helloHttpServer")
          .receive()
          .post("/test")
          .payload("<testRequestMessage>" +
                    "<text<Hello HttpServer</text>" +
                  "</testRequestMessage>")
          .contentType("application/xml")
          .accept("application/xml, */*")
          .header("X-CustomHeaderId", "${custom_header_id}")
          .header("Authorization", "Basic c29tZVVzZXJuYW1lOnNvbWVQYXNzd29yZA==")
          .extractFromHeader("X-MessageId", "message_id");

    http().server("helloHttpServer")
          .send()
          .response(HttpStatus.OK)
          .payload("<testResponseMessage>" +
                    "<text<Hello Citrus</text>" +
                  "</testResponseMessage>")
          .version("HTTP/1.1")
          .contentType("application/xml")
          .header("X-CustomHeaderId", "${custom_header_id}")
          .header("X-MessageId", "${message_id}");
}

This is the exact same example in Java DSL. We select server actions first and receive client requests. Then we send back a response with a HttpStatus.OK status. This completes the server actions on Http message transport. Now we continue with some more Http specific settings and features.

18.4. HTTP headers

When dealing with HTTP request/response communication we always deal with HTTP specific headers. The HTTP protocol defines a group of header attributes that both client and server need to be able to handle. You can set and validate these HTTP headers in Citrus quite easy. Let us have a look at a client operation in Citrus where some HTTP headers are explicitly set before the request is sent out.

<http:send-request client="httpClient">
  <http:POST>
    <http:headers>
        <http:header name="X-CustomHeaderId" value="${custom_header_id}"/>
        <http:header name="Content-Type" value="text/xml"/>
        <http:header name="Accept" value="text/xml,*/*"/>
    </http:headers>
    <http:body>
        <http:payload>
            <testRequestMessage>
                <text>Hello HttpServer</text>
            </testRequestMessage>
        </http:payload>
    </http:body>
  </http:POST>
</http:send-request>

We are able to set custom headers (X-CustomHeaderId) that go directly into the HTTP header section of the request. In addition to that testers can explicitly set HTTP reserved headers such as Content-Type . Fortunately you do not have to set all headers on your own. Citrus will automatically set the required HTTP headers for the request. So we have the following HTTP request which is sent to the server:

POST /test HTTP/1.1
Accept: text/xml, */*
Content-Type: text/xml
X-CustomHeaderId: 123456789
Accept-Charset: macroman
User-Agent: Jakarta Commons-HttpClient/3.1
Host: localhost:8091
Content-Length: 175
<testRequestMessage>
    <text>Hello HttpServer</text>
</testRequestMessage>

On server side testers are interested in validating the HTTP headers. Within Citrus receive action you simply define the expected header entries. The HTTP specific headers are automatically available for validation as you can see in this example:

<http:receive-request server="httpServer">
  <http:POST>
    <http:headers>
        <http:header name="X-CustomHeaderId" value="${custom_header_id}"/>
        <http:header name="Content-Type" value="text/xml"/>
        <http:header name="Accept" value="text/xml,*/*"/>
    </http:headers>
    <http:body>
        <http:payload>
            <testRequestMessage>
                <text>Hello HttpServer</text>
            </testRequestMessage>
        </http:payload>
    </http:body>
  </http:POST>
</http:receive-request>

The test checks on custom headers and HTTP specific headers to meet the expected values.

Now that we have accepted the client request and validated the contents we are able to send back a proper HTTP response message. Same thing here with HTTP specific headers. The HTTP protocol defines several headers marking the success or failure of the server operation. In the test case you can set those headers for the response message with conventional Citrus header names. See the following example to find out how that works for you.

<http:send-response server="httpServer">
    <http:headers status="200" reason-phrase="OK">
        <http:header name="X-CustomHeaderId" value="${custom_header_id}"/>
        <http:header name="Content-Type" value="text/xml"/>
    </http:headers>
    <http:body>
        <http:payload>
            <testResponseMessage>
                <text>Hello Citrus Client</text>
            </testResponseMessage>
        </http:payload>
    </http:body>
</http:send-response>

Once more we set the custom header entry (X-CustomHeaderId) and a HTTP reserved header (Content-Type) for the response message. On top of this we are able to set the response status for the HTTP response. We use the reserved header names status in order to mark the success of the server operation. With this mechanism we can easily simulate different server behaviour such as HTTP error response codes (e.g. 404 - Not found, 500 - Internal error). Let us have a closer look at the generated response message:

HTTP/1.1 200 OK
Content-Type: text/xml;charset=UTF-8
Accept-Charset: macroman
Content-Length: 205
Server: Jetty(7.0.0.pre5)
<testResponseMessage>
    <text>Hello Citrus Client</text>
</testResponseMessage>
You do not have to set the reason phrase all the time. It is sufficient to only set the HTTP status code. Citrus will automatically add the proper reason phrase for well known HTTP status codes.

The only thing that is missing right now is the validation of HTTP status codes when receiving the server response in a Citrus test case. It is very easy as you can use the Citrus reserved header names for validation, too.

<http:receive-response client="httpClient">
    <http:headers status="200" reason-phrase="OK" version="HTTP/1.1">
        <http:header name="X-CustomHeaderId" value="${custom_header_id}"/>
    </http:headers>
    <http:body>
        <http:payload>
            <testResponseMessage>
                <text>Hello Test Framework</text>
            </testResponseMessage>
        </http:payload>
    </http:body>
</http:receive-response>

Up to now we have used some of the basic Citrus reserved HTTP header names (status, version, reason-phrase). In HTTP RESTful services some other header names are essential for validation. These are request attributes like query parameters, context path and request URI. The Citrus server side REST message controller will automatically add all this information to the message header for you. So all you need to do is validate the header entries in your test.

The next example receives a HTTP GET method request on server side. Here the GET request does not have any message payload, so the validation just works on the information given in the message header. We assume the client to call http://localhost:8080/app/users?id=123456789. As a tester we need to validate the request method, request URI, context path and the query parameters.

<http:receive-request server="httpServer">
  <http:GET path="/app/users" context-path="/app">
    <http:params>
        <http:param name="id" value="123456789"/>
    </http:params>
    <http:headers>
        <http:header name="Host" value="localhost:8080"/>
        <http:header name="Content-Type" value="text/html"/>
        <http:header name="Accept" value="text/xml,*/*"/>
    </http:headers>
    <http:body>
        <http:data></http:data>
    </http:body>
  </http:GET>
</http:receive-request>
Be aware of the slight differences in request URI and context path. The context path gives you the web application context path within the servlet container for your web application. The request URI always gives you the complete path that was called for this request.

As you can see we are able to validate all parts of the initial request endpoint URI the client was calling. This completes the HTTP header processing within Citrus. On both client and server side Citrus is able to set and validate HTTP specific header entries which is essential for simulating HTTP communication.

18.5. HTTP server interceptors

The server component is able to add custom interceptors that participate in the request/response processing. The interceptors need to implement the common interface org.springframework.web.servlet.HandlerInterceptor.

<citrus-http:server id="httpServer"
                  port="8080"
                  auto-start="true"
                  interceptors="serverInterceptors"/>

<util:list id="serverInterceptors">
  <bean class="com.consol.citrus.http.interceptor.LoggingHandlerInterceptor"/>
</util:list>

The sample above adds the Citrus logging handler interceptor that logs requests and responses exchanged with that server component. You can add custom interceptor implementations here in order to participate in the request/response message processing.

18.6. HTTP form urlencoded data

HTML form data can be sent to the server using different methods and content types. One of them is a POST method with x-www-form-urlencoded body content. The form data elements are sent to the server using key-value pairs POST data where the form control name is the key and the control data is the url encoded value.

Form urlencoded form data content could look like this:

password=s%21cr%21t&username=foo

A you can see the form data is automatically encoded. In the example above we transmit two form controls password and username with respective values s$cr$t and foo . In case we would validate this form data in Citrus we are able to do this with plaintext message validation.

<receive endpoint="httpServer">
  <message type="plaintext">
    <data>
      <![CDATA[
        password=s%21cr%21t&username=${username}
      ]]>
    </data>
  </message>
  <header>
    <element name="citrus_http_method" value="POST"/>
    <element name="citrus_http_request_uri" value="/form-test"/>
    <element name="Content-Type" value="application/x-www-form-urlencoded"/>
  </header>
</receive>

Obviously validating these key-value pair character sequences can be hard especially when having HTML forms with lots of form controls. This is why Citrus provides a special message validator for x-www-form-urlencoded contents. First of all we have to add citrus-http module as dependency to our project if not done so yet. After that we can add the validator implementation to the list of message validators used in Citrus.

<citrus:message-validators>
  <citrus:validator class="com.consol.citrus.http.validation.FormUrlEncodedMessageValidator"/>
</citrus:message-validators>

Now we are able to receive the urlencoded form data message in a test.

<receive endpoint="httpServer">
  <message type="x-www-form-urlencoded">
    <payload>
      <form-data xmlns="http://www.citrusframework.org/schema/http/message">
        <content-type>application/x-www-form-urlencoded</content-type>
        <action>/form-test</action>
        <controls>
          <control name="password">
            <value>${password}</value>
          </control>
          <control name="username">
            <value>${username}</value>
          </control>
        </controls>
      </form-data>
    </payload>
  </message>
  <header>
    <element name="citrus_http_method" value="POST"/>
    <element name="citrus_http_request_uri" value="/form-test"/>
    <element name="Content-Type" value="application/x-www-form-urlencoded"/>
  </header>
</receive>

We use a special message type x-www-form-urlencoded so the new message validator will take action. The form url encoded message validator is able to handle a special XML representation of the form data. This enables the very powerful XML message validation capabilities of Citrus such as ignoring elements and usage of test variables inline.

Each form control is translated to a control element with respective name and value properties. The form data is validated in a more comfortable way as the plaintext message validator would be able to offer.

18.7. HTTP error handling

So far we have received response messages with HTTP status code 200 OK . How to deal with server errors like 404 Not Found or 500 Internal server error ? The default HTTP message client error strategy is to propagate server error response messages to the receive action for validation. We simply check on HTTP status code and status text for error validation.

<http:send-request client="httpClient">
    <http:body>
        <http:payload>
            <testRequestMessage>
                <text>Hello HttpServer</text>
            </testRequestMessage>
        </http:payload>
    </http:body>
</http:send-request>

<http:receive-request client="httpClient">
    <http:body>
        <http:data><![CDATA[]]></http:data>
    </http:body>
    <http:headers status="403" reason-phrase="FORBIDDEN"/>
</http:receive>

The message data can be empty depending on the server logic for these error situations. If we receive additional error information as message payload just add validation assertions as usual.

Instead of receiving such empty messages with checks on HTTP status header information we can change the error strategy in the message sender component in order to automatically raise exceptions on response messages other than 200 OK . Therefore we go back to the HTTP message sender configuration for changing the error strategy.

<citrus-http:client id="httpClient"
                              request-url="http://localhost:8080/test"
                              error-strategy="throwsException"/>

Now we expect an exception to be thrown because of the error response. Following from that we have to change our test case. Instead of receiving the error message with receive action we assert the client exception and check on the HTTP status code and status text.

<assert exception="org.springframework.web.client.HttpClientErrorException"
           message="403 Forbidden">
    <when>
        <http:send-request client="httpClient">
            <http:body>
                <http:payload>
                    <testRequestMessage>
                        <text>Hello HttpServer</text>
                    </testRequestMessage>
                </http:payload>
            </http:body>
        </http:send-request>
    </when>
</assert>

Both ways of handling HTTP error messages on client side are valid for expecting the server to raise HTTP error codes. Choose the preferred way according to your test project requirements.

18.8. HTTP client basic authentication

As client you may have to use basic authentication in order to access a resource on the server. In most cases this will be username/password authentication where the credentials are transmitted in the request header section as base64 encoding.

The easiest approach to set the Authorization header for a basic authentication HTTP request would be to set it on your own in the send action definition. Of course you have to use the correct basic authentication header syntax with base64 encoding for the username:password phrase. See this simple example.

<http:headers>
    <http:header name="Authorization" value="Basic c29tZVVzZXJuYW1lOnNvbWVQYXNzd29yZA=="/>
</http:headers>

Citrus will add this header to the HTTP requests and the server will read the Authorization username and password. For more convenient base64 encoding you can also use a Citrus function, see functions-encode-base64

Now there is a more comfortable way to set the basic authentication header in all the Citrus requests. As Citrus uses Spring’s REST support with the RestTemplate and ClientHttpRequestFactory the basic authentication is already covered there in a more generic way. You simply have to configure the basic authentication credentials on the RestTemplate’s ClientHttpRequestFactory. Just see the following example and learn how to do that.

<citrus-http:client id="httpClient"
                    request-method="POST"
                    request-url="http://localhost:8080/test"
                    request-factory="basicAuthFactory"/>

<bean id="basicAuthFactory"
    class="com.consol.citrus.http.client.BasicAuthClientHttpRequestFactory">
  <property name="authScope">
      <bean class="org.apache.http.auth.AuthScope">
        <constructor-arg value="localhost"/>
        <constructor-arg value="8072"/>
        <constructor-arg value=""/>
        <constructor-arg value="basic"/>
      </bean>
  </property>
  <property name="credentials">
    <bean class="org.apache.http.auth.UsernamePasswordCredentials">
        <constructor-arg value="someUsername"/>
        <constructor-arg value="somePassword"/>
    </bean>
  </property>
</bean>

The advantages of this method is obvious. Now all sending test actions that reference the client component will automatically add the basic authentication header.

Since Citrus has upgraded to Spring 3.1.x the Jakarta commons HTTP client is deprecated with Citrus version 1.2. The formerly used UserCredentialsClientHttpRequestFactory is therefore also deprecated and will not continue with next versions. Please update your configuration if you are coming from Citrus 1.1 or earlier versions.

The above configuration results in HTTP client requests with authentication headers properly set for basic authentication. The client request factory takes care on adding the proper basic authentication header to each request that is sent with this Citrus message sender. Citrus uses preemptive authentication. The message sender only sends a single request to the server with all authentication information set in the message header. The request which determines the authentication scheme on the server is skipped. This is why you have to add some auth scope in the client request factory so Citrus can setup an authentication cache within the HTTP context in order to have preemptive authentication.

As a result of the basic auth client request factory the following example request that is created by the Citrus HTTP client has the Authorization header set. This is done now automatically for all requests with this HTTP client.

POST /test HTTP/1.1
Accept: text/xml, */*
Content-Type: text/xml
Accept-Charset: iso-8859-1, us-ascii, utf-8
Authorization: Basic c29tZVVzZXJuYW1lOnNvbWVQYXNzd29yZA==
User-Agent: Jakarta Commons-HttpClient/3.1
Host: localhost:8080
Content-Length: 175
<testRequestMessage>
  <text>Hello HttpServer</text>
</testRequestMessage>

18.9. HTTP server basic authentication

Citrus as a server can also set basic authentication so clients need to authenticate properly when accessing server resources.

<citrus-http:server id="basicAuthHttpServer"
                port="8090"
                auto-start="true"
                resource-base="src/it/resources"
                security-handler="basicAuthSecurityHandler"/>

<bean id="basicAuthSecurityHandler" class="com.consol.citrus.http.security.SecurityHandlerFactory">
    <property name="users">
        <list>
            <bean class="com.consol.citrus.http.security.User">
                <property name="name" value="citrus"/>
                <property name="password" value="secret"/>
                <property name="roles" value="CitrusRole"/>
            </bean>
        </list>
    </property>
    <property name="constraints">
        <map>
            <entry key="/foo/*">
                <bean class="com.consol.citrus.http.security.BasicAuthConstraint">
                    <constructor-arg value="CitrusRole"/>
                </bean>
            </entry>
        </map>
    </property>
</bean>

We have set a security handler on the server web container with a constraint on all resources with /foo/*. Following from that the server requires basic authentication for these resources. The granted users and roles are specified within the security handler bean definition. Connecting clients have to set the basic auth HTTP header properly using the correct user and role for accessing the Citrus server now.

You can customize the security handler for your very specific needs (e.g. load users and roles with JDBC from a database). Just have a look at the code base and inspect the settings and properties offered by the security handler interface.

This mechanism is not restricted to basic authentication only. With other settings you can also set up digest or form-based authentication constraints very easy.

18.10. HTTP cookies

Cookies hold any kind of information and are saved as test information on the client side. Http servers are able to instruct the client (browser) to save a new cookie with name, value and some attributes. This is usually done with a "Set-Cookie" message header set on the server response message. Citrus is able to add those cookie information in a server response.

XML DSL
<http:receive-request server="echoHttpServer">
    <http:POST>
        <http:headers>
            <http:header name="Operation" value="getCookie"/>
        </http:headers>
    <http:body>
        <http:data>
            <![CDATA[
            Some request data
            ]]>
        </http:data>
    </http:body>
    </http:POST>
</http:receive-request>

<http:send-response server="echoHttpServer">
    <http:headers status="200" reason-phrase="OK" version="HTTP/1.1">
        <http:header name="Operation" value="getCookie"/>
        <http:cookie name="Token"
                     value="${messageId}"
                     secure="false"
                     domain="citrusframework.org"
                     path="/test/cookie.py"
                     max-age="86400"/>
    </http:headers>
    <http:body>
        <http:data>
            <![CDATA[
            Some response body
            ]]>
        </http:data>
    </http:body>
</http:send-response>
Java DSL
Cookie cookie = new Cookie("Token", "${messageId}");
cookie.setPath("/test/cookie.py");
cookie.setSecure(false);
cookie.setDomain("citrusframework.org");
cookie.setMaxAge(86400);

http().server("echoHttpServer")
 .receive()
 .post()
 .payload("Some request data")
 .header("Operation", "sayHello");

http().server("echoHttpServer")
 .send()
 .response(HttpStatus.OK)
 .payload("Some response body")
 .header("Operation", "sayHello")
 .cookie(cookie);

The sample above receives a Http request with method POST and some request data. The server response is specified with Http 200 OK and some additional cookie information. The cookie is part of the message header specification and gets a name and value as well as several other attributes. This response will result in a Http response with the "Set-Cookie" header set:

Set-Cookie:Token=5877643571;Path=/test/cookie.py;Domain=citrusframework.org;Max-Age=86400

As you can see test variables are replaced before the cookie is added to the response. The client now is able to receive the cookie information for validation:

XML DSL
<http:receive-response server="echoHttpClient">
  <http:headers status="200" reason-phrase="OK" version="HTTP/1.1">
  <http:header name="Operation" value="getCookie"/>
  <http:cookie name="Token"
               value="${messageId}"
               secure="false"
               domain="citrusframework.org"
               path="/test/cookie.py"
               max-age="86400"/>
  </http:headers>
  <http:body>
    <http:data>
      <![CDATA[
        Some response body
      ]]>
    </http:data>
  </http:body>
</http:receive-response>
Java DSL
Cookie cookie = new Cookie("Token", "${messageId}");
cookie.setPath("/test/cookie.py");
cookie.setSecure(false);
cookie.setDomain("citrusframework.org");
cookie.setMaxAge(86400);

http().client("echoHttpClient")
 .receive()
 .response(HttpStatus.OK)
 .payload("Some response body")
 .header("Operation", "sayHello")
 .cookie(cookie);

Once again the cookie information is added to the header specification. The Citrus message validation will make sure that the cookie information is present with all specified attributes.

In all further actions the client is able to continue to send the cookie information with name and value:

XML DSL
<http:send-request client="echoHttpClient" fork="true">
  <http:POST>
    <http:headers>
      <http:header name="Operation" value="sayHello"/>
      <http:cookie name="Token" value="${messageId}"/>
    </http:headers>
    <http:body>
      <http:data>
        <![CDATA[
          Some other request data
        ]]>
      </http:data>
    </http:body>
  </http:POST>
</http:send-request>
Java DSL
http().client("echoHttpClient")
    .send()
    .post()
    .fork(true)
    .payload("Some other request data")
    .header("Operation", "sayHello")
    .cookie(new Cookie("Token", "${messageId}"));

The cookie now is only specified with name and value as the cookie now goes to the "Cookie" request message header.

Cookie:Token=5877643571

Of course the Citrus Http server can now also validate the cookie information in a request validation:

XML DSL
<http:receive-request client="echoHttpServer">
  <http:POST>
    <http:headers>
      <http:header name="Operation" value="sayHello"/>
      <http:cookie name="Token" value="${messageId}"/>
    </http:headers>
    <http:body>
      <http:data>
        <![CDATA[
          Some other request data
        ]]>
      </http:data>
    </http:body>
  </http:POST>
</http:receive-request>
Java DSL
http().server("echoHttpServer")
    .receive()
    .post()
    .payload("Some other request data")
    .header("Operation", "sayHello")
    .cookie(new Cookie("Token", "${messageId}"));

The Citrus message validation will make sure that the cookie is set in the request with respective name and value.

18.11. HTTP Gzip compression

Gzip is a very popular compression mechanism for optimizing the message transportation for large content. The Citrus http client and server components support gzip compression out of the box. This means that you only need to set the specific encoding headers in your http request/response message.

Accept-Encoding=gzip

Setting for clients when requesting gzip compressed response content. The Http server must support gzip compression then in order to provide the response as zipped byte stream. The Citrus http server component automatically recognizes this header in a request and applies gzip compression to the response.

Content-Encoding=gzip

When a http server sends compressed message content to the client this header is set to gzip in order to mark the compression. The Http client must support gzip compression then in order to unzip the message content. The Citrus http client component automatically recognizes this header in a response and applies gzip unzip logic before passing the message to the test case.

The Citrus client and server automatically take care on gzip compression when those headers are set. In the test case you do not need to zip or unzip the content then as it is automatically done before.

This means that you can request gzipped content from a server with just adding the message header Accept-Encoding in your http request operation.

<echo>
  <message>Send Http client request for gzip compressed data</message>
</echo>

<http:send-request client="gzipClient">
    <http:POST>
      <http:headers content-type="text/html">
        <http:header name="Accept-Encoding" value="gzip"/>
        <http:header name="Accept" value="text/plain"/>
      </http:headers>
</http:POST>
</http:send-request>

<echo>
  <message>Receive text automatically gzip unzipped</message>
</echo>

<http:receive-response client="gzipClient">
    <http:headers status="200" reason-phrase="OK">
      <http:header name="Content-Type" value="text/plain"/>
    </http:headers>
    <http:body type="plaintext">
      <http:data>${text}</http:data>
    </http:body>
</http:receive-response>

On the server side if we receive a message and the response should be compressed with Gzip we just have to set the Content-Encoding header in the response operation.

<echo>
  <message>Receive gzip compressed as base64 encoded text</message>
</echo>

<http:receive-request server="echoHttpServer">
    <http:POST path="/echo">
      <http:headers>
        <http:header name="Content-Type" value="text/html"/>
        <http:header name="Accept-Encoding" value="gzip"/>
        <http:header name="Accept" value="text/plain"/>
      </http:headers>
    </http:POST>
</http:receive-request>

<echo>
  <message>Send Http server gzip compressed response</message&