Version: 4.3.3

citrus-logo

1. Preface

Integration tests are a critical part of software testing. In contrast to unit tests where the primary goal is to verify a single class or method in isolation to other parts of the software the integration tests look at a wider scope with several components and software parts interacting with each other.

Integration tests often rely on infrastructure components such as I/O, databases, 3rd party services and so on. In combination with messaging and multiple message transports as client and/or server the automated tests become a tough challenge. Testers need sufficient tool support to master this challenge for automated integration test. Citrus as an Open Source framework is here to help you master this challenge.

In a typical enterprise application and middleware scenario automated integration testing of message-based interfaces is exhausting and sometimes barely possible. Usually the tester is forced to simulate several interface partners in an end-to-end integration test.

The first thing that comes to one’s mind is manual testing. No doubt manual testing is fast. In a long term perspective manual testing is time-consuming and causes severe problems regarding maintainability as they are error prone and not repeatable.

Citrus provides a complete test automation tool for integration testing of message-based enterprise applications. You can test your message interfaces (Http REST, SOAP, JMS, Kafka, TCP/IP, FTP, …​) to other applications as client and server. Every time a code change is made all automated Citrus tests ensure the stability of software interfaces and its message communication.

Regression testing and continuous integration is very easy as Citrus fits into your build lifecycle (Maven or Gradle) as usual Java unit test (JUnit, TestNG, Cucumber).

With powerful validation capabilities for various message formats like XML, CSV or JSON Citrus is ready 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 open, please do not hesitate to give feedback in form of comments, change requests, fixes and pull requests. We are more than happy to continuously improve this documentation with your help!

2. Introduction

Citrus provides automated integration tests for message-based enterprise applications. The framework is Open Source and supports various messaging transports (Http REST, SOAP, JMS, Kafka, TCP/IP, FTP, …​) and data formats (XML, Json, plaintext, binary).

The Citrus tests use well-known unit test frameworks (JUnit, TestNG, Cucumber) for execution and integrates with build tools like Maven or Gradle. In addition, Citrus leverages standard libraries like Quarkus, the Spring Framework or Apache Camel.

It is also good to know that Citrus is written purely in Java. Thus, it is also fully compatible with Kotlin.

2.1. Overview

Citrus supports 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 act both 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 framework 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, delays, error simulation, scripting and many more).

The test is able 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.

You can choose how to describe the test case definition either with pure XML or a Java domain specific language. The tests can be executed multiple times as automated integration test.

With JUnit and TestNG integration Citrus can easily be integrated into your build lifecycle process (Maven or Gradle). 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 body) for XML, CSV, SOAP, JSON or plaintext messages Citrus is able to validate the incoming data towards syntax and semantics.

2.2. Usage scenarios

Citrus should help you whenever it comes to verify a message-based software with its interfaces to other components and partners using automated integration tests. Every project that interacts with other components over messaging transports needs to simulate these interface partners on the client or server side in a test scenario. Citrus is here to help you master these test automation tasks.

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 messaging interfaces to other applications. A client application invokes services on the SUT and triggers business logic. The SUT is linked to several backend applications over various messaging transports (here SOAP, JMS, and Http). As part of the business logic one or more of these backend services is called and interim message notifications and responses are sent back to the client application.

This generates a bunch of messages that are exchanged throughout the components 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). In addition to messaging steps Citrus is also able to perform arbitrary other test actions (e.g. perform a database query between requests).

In fact a Citrus test case is nothing but a normal JUnit or TestNG test case. This makes it very straight forward to run the tests from your favorite Java IDE (Eclipse, IntelliJ, VSCode, …​) and as part of your software build process (Maven or Gradle). The Citrus tests become repeatable and give you fully automated integration tests to ensure software quality and interface stability.

The following reference guide walks through all Citrus capabilities and shows how to have a great integration test experience.

3. 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 add Citrus as a test-scoped dependency library in your project. Build tools like Maven or Gradle provide standard integration for test libraries such as Citrus. As Citrus tests are nothing but normal unit tests (JUnit, TestNG, Cucumber) you can run the tests with the standard unit test build integration (e.g. via maven-failsafe plugin).

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

3.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.

In case you already use Maven in your project you can just add 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 and find out how to set up a Maven project.

Assuming you have a proper Maven project setup you can 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)

Add Citrus base dependency
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-base</artifactId>
  <version>${citrus.version}</version>
  <scope>test</scope>
</dependency>
  • The dependency above adds the base functionality of Citrus. You need to add modules as you require them.

Add modules as required in your project. For instance Http support
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-http</artifactId>
  <version>${citrus.version}</version>
  <scope>test</scope>
</dependency>
Choose test runtime (JUnit, TestNG, Cucumber) that is used to run the tests.
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-testng</artifactId>
  <version>${citrus.version}</version>
  <scope>test</scope>
</dependency>
  • Citrus integrates nicely with the Spring framework. In case you want to use the Spring dependency injection and bean configuration capabilities just add the Spring support in Citrus.

Add Spring support
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-spring</artifactId>
  <version>${citrus.version}</version>
  <scope>test</scope>
</dependency>
  • Also, Citrus provides a Maven plugin that you can add. The plugin provides some convenience functionalities such as creating new tests from command line.

Add Citrus Maven plugin
<plugin>
  <groupId>org.citrusframework.mvn</groupId>
  <artifactId>citrus-maven-plugin</artifactId>
  <version>${citrus.version}</version>
  <configuration>
    <author>Donald Duck</author>
    <targetPackage>org.citrusframework</targetPackage>
  </configuration>
</plugin>

The Maven project is now ready to use Citrus. You can start writing new test cases with the Citrus Maven plugin:

Create new test
mvn citrus:create-test

The command above starts an interactive command line interface that helps you to create a 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 project’s integration-test phase using the Maven failsafe plugin. Here is a sample failsafe configuration for Citrus.

Maven failsafe plugin
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-failsafe-plugin</artifactId>
  <version>${maven.failsafe.version}</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 source directory src/test/java and src/test/resources.

You are now ready to call the usual Maven verify goal (mvn verify) in order to build your project and run the tests. The Citrus integration tests are executed automatically during the build process.

Run all tests with Maven
mvn verify
Run single test by its name
mvn verify -Dit.test=MyFirstCitrusIT
The Maven failsafe plugin by default executes tests with specific name pattern. This is because integration tests should not execute in Maven unit test phase, too. Your 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 https://citrusframework.org/tutorials.html.

3.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.

The Gradle build configuration goes to the build.gradle and settings.gradle files. The files define the project name and the project version.

Gradle project configuration
rootProject.name = 'citrus-sample-gradle'
group 'org.citrusframework.samples'
version '${citrus.version}'

The Citrus libraries are available on Maven central repository. This means you should add this repository so Gradle knows how to download the required Citrus artifacts.

Add Maven central repository
repositories {
    mavenCentral()
}

Citrus stable release versions are available on Maven central. Now let’s move on with adding the Citrus libraries to the project.

Add Citrus test scoped dependencies
dependencies {
    testCompile group: 'org.citrusframework', name: 'citrus-base', version: '${citrus.version}'
    testCompile group: 'org.citrusframework', name: 'citrus-http', version: '${citrus.version}'
    testCompile group: 'org.testng', name: 'testng', version: '6.11'
    [...]
}

Citrus provides various modules that encapsulate different functionalities. The citrus-base module is the basis and holds core functionality. In addition, you may add further modules that match your project needs (e.g. add Http support with citrus-http).

As a runtime the project chose to use TestNG. You can also use JUnit or Cucumber as a test runtime. Each of those frameworks integrates seamlessly with the Gradle build.

Choose test runtime provider
test {
    useTestNG()
}

Of course JUnit is also supported. This completes the Gradle build configuration settings. You can move on to writing some Citrus integration tests and add those to src/test/java directory.

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

Run the build with Gradle
gradlew clean build

This executes all Citrus test cases during the build. You will be able to see Citrus performing some integration test logging output.

If you just want to execute all tests you can call:

Run all tests
gradlew clean check

Of course, you can also run the Citrus tests from your favorite Java IDE. Just start the Citrus test as a normal unit test using the Gradle integration in IntelliJ, Eclipse or VSCode.

4. Runtimes

A Citrus test case is nothing but Java unit test leveraging well-known standard tools such as JUnit, TestNG or Cucumber as a runtime.

Chances are very high that Java developers are familiar with at least one of the standard tools. Everything you can do with JUnit and TestNG you can do with Citrus tests as well (e.g. Maven build integration, run tests from your favorite IDE, include tests into a continuous build tool).

Why is Citrus related to unit test frameworks although it represents a framework for integration testing? The answer to this question is quite simple: This is because Citrus wants to benefit from standard libraries such as JUnit and TestNG for Java test execution. Both unit testing frameworks offer various ways of execution and are widely supported by other tools (e.g. continuous build, build lifecycle, development IDE).

You can write the Citrus test code in a Java domain specific language or in form of an XML test declaration file that gets loaded as part of the test. The Java domain specific language in Citrus is a set of classes and methods to leverage the test code in form of a fluent API. Users can simply configure a test action with the different options using a fluent builder pattern style DSL.

Citrus Java DSL
@CitrusTest(name = "Hello_IT")
public void helloTest() {
    given(
        variable("user", "Citrus")
    );

    then(
        echo().message("Hello ${user}!"
    ));
}

The sample above is a very simple Citrus test that creates a test variable and prints a message to the console. The Java DSL you write is the same for all runtimes (JUnit, TestNG, Cucumber, etc.) and should help you to also solve very complex test scenarios.

The following sections have a closer look at the different runtimes for Citrus.

4.1. 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 define tests as executable TestNG Java classes.

The TestNG support is shipped in a separate Maven module. You need to include the module as a dependency in your project.
TestNG module dependency
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-testng</artifactId>
  <version>${citrus.version}</version>
</dependency>

4.1.1. TestNG tests

See the following sample showing how to write a Citrus test on top of TestNG:

TestNG Citrus test
package org.citrusframework.samples;

import org.testng.annotations.Test;
import org.citrusframework.annotations.CitrusTest;
import org.citrusframework.testng.TestNGCitrusSupport;

@Test
public class Simple_IT extends TestNGCitrusSupport {

    @CitrusTest(name = "Simple_IT")
    public void simpleTest() {
        description("First example showing the basic Java DSL!");

        given(
            variable("user", "Citrus")
        );

        then(
            echo().message("Hello ${user}!"
        ));
    }
}

If you are familiar with TestNG you will see that the Java class is a normal TestNG test class using the usual @Test annotation. For convenience reasons you can extend a basic Citrus TestNG base class TestNGCitrusSupport which enables the Citrus test execution as well as the Java DSL features for us.

You can also combine Citrus with the Spring framework and its dependency injection and IoC capabilities. In order to enable Spring support in Citrus add the citrus-spring module to your project and extend TestNGCitrusSpringSupport as a base class. With the Spring support in Citrus the test is able to use @Autowired annotations for injecting Spring beans into the test class and you can define the Spring application context with @Configuration annotations for instance.

In addition, the test methods use the @CitrusTest annotation which allows setting properties such as test names and packages.

The Citrus test logic goes directly as the method body with using the Citrus Java domain specific language features. As you can see the Java DSL is able to follow BDD (Behavior Drive Design) principles with Given-When-Then syntax. As an alternative to that you can just use run() for all test actions.

Pure test action DSL
@CitrusTest(name = "Simple_IT")
public void simpleTest() {
    description("First example showing the basic Java DSL!");

    run(variable("user", "Citrus"));

    run(
        echo().message("Hello ${user}!"
    ));
}

The great news is that you can still use the awesome TestNG features in with the Citrus test class (e.g. 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:

Set test groups
@Test(groups = {"long-running"})
public void longRunningTest() {
    ...
}

For more information on TestNG please visit the official TestNG website, where you find a complete reference documentation. The following sections deal with a subset of these TestNG features in particular.

4.1.2. Use TestNG data providers

TestNG as a framework comes with lots of great features such as data providers. Data providers execute a test case several times with different test data. Each test execution works with a specific parameter value. You can use data provider parameter values as test variables in Citrus. See the next listing on how to use TestNG data providers in Citrus:

TestNG Citrus data provider test
public class DataProviderIT extends TestNGCitrusSupport {

    @CitrusTest
    @CitrusParameters({"message", "delay"})
    @Test(dataProvider = "messageDataProvider")
    public void dataProvider(String message, Long sleep) {
        run(echo(message));
        run(sleep().milliseconds(sleep));

        run(echo("${message}"));
        run(echo("${delay}"));
    }

    @DataProvider
    public Object[][] messageDataProvider() {
        return new Object[][] {
                { "Hello World!", 300L },
                { "Citrus rocks!", 1000L },
                { "Hi from Citrus!", 500L },
        };
    }
}

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 test variables called message and delay.

With Sharding

TestNG data provider based tests can additionally be run in a distributed manner, facilitating test sharding. The following snipped showcases you how to get started within a Spring test case environment.

Sharded TestNG Citrus data provider test
import static org.citrusframework.common.TestLoader.SPRING;
import static org.citrusframework.sharding.Shard.createShard;

import org.citrusframework.annotations.CitrusTestSource;
import org.citrusframework.testng.CitrusParameters;
import org.citrusframework.testng.spring.TestNGCitrusSpringSupport;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class DataProviderIT extends TestNGCitrusSupport {

    @CitrusParameters({"message", "delay"})
    @Test(dataProvider = "messageDataProvider")
    @CitrusTestSource(type = SPRING, name = "DataProviderIT")
    public void dataProvider(String message, Long sleep) {
        run(echo(message));
        run(sleep().milliseconds(sleep));
    }

    @DataProvider("messageDataProvider")
    public Object[][] messageDataProvider() {
        return createShard(
            new Object[][] {
                { "Hello World!", 300L },
                { "Citrus rocks!", 1000L },
                { "Hi from Citrus!", 500L },
            }
        );
    }
}

All the available configuration options are documented withing the chapter Sharding Test Cases

4.1.3. Run tests in parallel

Integration tests tend to be more time-consuming compared to pure unit tests when it comes to execute tests. This is because integration tests often need to initialize test infrastructure (e.g. test servers, database connections). Running tests in parallel can reduce the overall test suite time a lot.

When running tests in parallel you need to make sure each test operates on its own set of resources. Tests must not share components such as the Citrus Java DSL test action runner or the test context.

You should be using the resource injection to make sure each test operates on its own resources.

Resource injection
public class ResourceInjection_IT extends TestNGCitrusSupport {

    @Test
    @CitrusTest
    public void injectResources(@Optional @CitrusResource TestCaseRunner runner,
                                @Optional @CitrusResource TestContext context) {

        runner.given(
            createVariable("random", "citrus:randomNumber(10)")
        );

        runner.run(
            echo("The random number is: ${random}")
        );
    }
}

First of all the method parameters must be annotated with @Optional because the values are not injected by TestNG itself but by the Citrus base test class. Finally, the parameter requires the @CitrusResource annotation in order to mark the parameter for Citrus resource injection.

Now each method uses its own resource instances which makes sure that parallel test execution can take place without having the risk of side effects on other tests running at the same time. Of course, you also need to make sure that the message exchange in your tests is ready to be performed in parallel (e.g. use message selectors).

4.2. JUnit5

With JUnit version 5 the famous unit test framework offers a new major version. The JUnit platform provides awesome extension points for other frameworks like Citrus to integrate with the unit testing execution.

Citrus provides extensions in order to enable Citrus related dependency injection and parameter resolving in your JUnit5 test.

The JUnit5 support is shipped in a separate Maven module. You need to include the module as a dependency in your project.
JUnit5 module dependency
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-junit5</artifactId>
  <version>${citrus.version}</version>
</dependency>

4.2.1. Citrus extension

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

JUnit5 Citrus test
package org.citrusframework.samples;

import org.citrusframework.GherkinTestActionRunner;
import org.citrusframework.annotations.CitrusTest;
import org.citrusframework.junit.jupiter.CitrusSupport;
import org.junit.jupiter.api.Test;

@CitrusSupport
public class Simple_IT {

    @Test
    @CitrusTest(name = "Simple_IT")
    public void simpleTest(@CitrusResource GherkinTestActionRunner runner) {
        runner.description("First example showing the basic Java DSL!");

        runner.given(
            variable("user", "Citrus")
        );

        runner.then(
            echo().message("Hello ${user}!"
        ));
    }
}

The class above is using the JUnit5 @Test annotation as a normal unit test would do. The @CitrusSupport annotation marks the test to use the Citrus JUnit5 extension. This enables us to use the @CitrusTest annotation on the test and adds support for the parameter injection for the TestActionRunner.

You can use the @CitrusSupport annotation, or you can use the classic @ExtendWith(CitrusExtension.class) annotation to enable the Citrus support for JUnit5.

The Citrus Java DSL runner is the entrance to the Java fluent API provided by Citrus. The sample above uses the Gherkin test runner variation for leveraging the BDD (Behavior Driven Development) style Given-When-Then syntax.

You can also inject the current TestContext in order to get access to the current test variables used by Citrus.

You can also combine Citrus with the Spring framework and its dependency injection and IoC capabilities. In order to enable Spring support in Citrus add the citrus-spring module to your project and use the @ExtendWith(CitrusSpringExtension.class) annotation. With the Spring support in Citrus the test is able to load components via the Spring application context.

4.2.2. Endpoint injection

In addition to injecting test resources you can also inject endpoints via @CitrusEndpoint annotated field injection in your test class. This enables you to inject endpoint components that are defined in the Citrus context configuration.

JUnit5 Citrus endpoint injection
package org.citrusframework.samples;

import org.citrusframework.annotations.*;
import org.citrusframework.GherkinTestActionRunner;
import org.citrusframework.junit.jupiter.CitrusSupport;
import org.citrusframework.http.client.HttpClient;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;

@CitrusSupport
public class Simple_IT {

    @CitrusEndpoint
    private HttpClient httpClient;

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

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

4.2.3. Citrus Spring extension

Spring is a famous dependency injection framework that also provides support for JUnit5. Citrus is able to load its components as Spring beans in an application context. The Citrus JUnit5 extension works great with the Spring extension.

The Spring extension loads the application context and Citrus adds all components to the Spring bean configuration.

JUnit5 Citrus Spring test
@CitrusSpringSupport
@ContextConfiguration(classes = CitrusSpringConfig.class)
public class SpringBean_IT {

    @Autowired
    private DirectEndpoint direct;

    @Test
    @CitrusTest
    void springBeanTest(@CitrusResource TestActionRunner actions) {
        actions.$(send().endpoint(direct)
                    .message()
                    .body("Hello from Citrus!"));

        actions.$(receive().endpoint(direct)
                    .message()
                    .body("Hello from Citrus!"));
    }
}

The test now uses the @CitrusSpringSupport annotation which combines the @ExtendWith(CitrusSpringExtension.class) and @ExtendWith(SpringExtension.class) annotation. This way the test combines the Spring application context management with the Citrus Java DSL functionality.

You can load Spring beans with @Autowired into your test. Also, you can use the @CitrusResource annotations to inject the test action runner fluent Java API.

The Spring application context should use the basic CitrusSpringConfig configuration class to load all Citrus components as Spring beans. You can customize the Spring application context by adding more configuration classes.

4.2.4. Test Sharding

You can run your JUnit5 test cases in order in a distributed manner, facilitating test sharding. The following snipped showcases you how to get started within a Spring/XML test case environment. Note that sharding is available with all kind of Stream-sources.

Sharded JUnit5 Spring/XML test
import static org.citrusframework.junit.jupiter.CitrusTestFactorySupport.springXml;
import static org.citrusframework.sharding.Shard.createShard;

import org.citrusframework.config.CitrusSpringConfig;
import org.citrusframework.junit.jupiter.spring.CitrusSpringSupport;
import org.citrusframework.junit.jupiter.spring.CitrusSpringXmlTestFactory;
import org.springframework.test.context.ContextConfiguration;

@CitrusSpringSupport
@ContextConfiguration(classes = {CitrusSpringConfig.class})
class SpringBeanXml_IT {

    @CitrusSpringXmlTestFactory
    Stream<DynamicTest> shardedSpringBeanXml_IT() {
        return createShard(
            springXml().packageScan("org.citrusframework.junit.jupiter.simple")
        );
    }
}

All the available configuration options are documented withing the chapter Sharding Test Cases

4.3. JUnit4

JUnit4 is still very popular and widely supported by many tools even though there is a new major version with JUnit5 already available. In general Citrus supports both JUnit4 and JUnit5 as test execution framework.

The JUnit4 support is shipped in a separate Maven module. You need to include the module as a dependency in your project.
JUnit4 module dependency
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-junit</artifactId>
  <version>${citrus.version}</version>
</dependency>

4.3.1. JUnit4 tests

See the following sample test class that uses JUnit4.

JUnit4 Citrus test
package org.citrusframework.samples;

import org.junit.Test;
import org.citrusframework.annotations.CitrusTest;
import org.citrusframework.junit.JUnit4CitrusSupport;

public class Simple_IT extends JUnit4CitrusSupport {

    @Test
    @CitrusTest(name = "Simple_IT")
    public void simpleTest() {
        description("First example showing the basic Java DSL!");

        given(
            variable("user", "Citrus")
        );

        then(
            echo().message("Hello ${user}!"
        ));
    }
}

The simple test class above uses the normal @Test annotation and extends the base class JUnit4CitrusSupport. This is the most convenient way to access the Citrus Java DSL capabilities. As an alternative you may switch to using the CitrusJUnit4Runner in your test class.

The fine thing here is that we are still able to use all JUnit features such as before/after hooks or ignoring tests.

After the test run the result 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 Gradle and so on.

You can also combine Citrus with the Spring framework and its dependency injection and IoC capabilities. In order to enable Spring support in Citrus add the citrus-spring module to your project and extend JUnit4CitrusSpringSupport as a base class. With the Spring support in Citrus the test is able to use @Autowired annotations for injecting Spring beans into the test class and you can define the Spring application context with @Configuration annotations for instance.

4.3.2. Run tests in parallel

Integration tests tend to be more time-consuming compared to pure unit tests when it comes to execute tests. This is because integration tests often need to initialize test infrastructure (e.g. test servers, database connections). Running tests in parallel can reduce the overall test suite time a lot.

When running tests in parallel you need to make sure each test operates on its own set of resources. Tests must not share components such as the Citrus Java DSL test action runner or the test context.

You should be using the resource injection to make sure each test operates on its own resources.

Resource injection
public class ResourceInjection_IT extends JUnit4CitrusSupport {

    @Test
    @CitrusTest
    public void injectResources(@CitrusResource TestCaseRunner runner,
                                @CitrusResource TestContext context) {

        runner.given(
            createVariable("random", "citrus:randomNumber(10)")
        );

        runner.run(
            echo("The random number is: ${random}")
        );
    }
}

The method parameters require the @CitrusResource annotations in order to mark the parameter for Citrus resource injection.

Now each method uses its own resource instances which makes sure that parallel test execution can take place without having the risk of side effects on other tests running at the same time. Of course, you also need to make sure that the message exchange in your tests is ready to be performed in parallel (e.g. use message selectors).

4.4. Cucumber

Behavior driven development (BDD) is a very popular concept when it comes to find a common understanding of test scopes test logic. The idea of defining and describing the software behavior as basis for all tests in prior to translating those feature descriptions into executable tests is a very interesting approach because it includes the technical experts as well as the domain experts.

With BDD the domain experts should be able to read and verify tests and the technical experts get a detailed description of what should happen in the test.

The test scenario descriptions follow the Gherkin syntax with a "Given-When-Then" structure. The Gherkin language is business readable and helps to explain business logic with help of concrete examples.

There are several frameworks in the Java community supporting BDD concepts. Citrus has dedicated support for the Cucumber framework because Cucumber is well suited for extensions and plugins. So with the Citrus and Cucumber integration you can write Gherkin syntax scenarios in order to run those as Citrus integration tests.

The Cucumber components in Citrus are located in a separate Maven module. You need to include the module as a Maven dependency to your project.
Cucumber module dependency
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-cucumber</artifactId>
  <version>${citrus.version}</version>
</dependency>

Cucumber works with both JUnit and TestNG as unit testing framework. You can choose which framework to use with Cucumber. So following from that we need a Maven dependency for the unit testing framework support:

Cucumber JUnit support
<dependency>
  <groupId>io.cucumber</groupId>
  <artifactId>cucumber-junit</artifactId>
  <version>${cucumber.version}</version>
</dependency>

In order to enable Citrus Cucumber support we need to specify a special object factory in the environment. The most comfortable way to specify a custom object factory is to add this property to the cucumber.properties in classpath.

cucumber.properties
cucumber.object-factory=org.citrusframework.cucumber.backend.CitrusObjectFactory

This special object factory takes care of creating all step definition instances. The object factory is able to inject @CitrusResource annotated fields in step classes. We will see this later on in the examples. The usage of this special object factory is mandatory in order to combine Citrus and Cucumber capabilities.

The CitrusObjectFactory will automatically initialize the Citrus world for us. This includes the default Citrus context configuration that is automatically loaded within the object factory. So you can define and use Citrus components as usual within your test.

After these preparation steps you are able to combine Citrus and Cucumber in your project.

In case you want to use Spring support in Citrus with a Spring application context you should use the following factory implementation.
cucumber.properties
cucumber.object-factory=org.citrusframework.cucumber.backend.spring.CitrusSpringObjectFactory

4.4.1. Cucumber options

Cucumber is able to run tests with JUnit. The basic test case is an empty test which uses the respective JUnit runner implementation from cucumber.

MyFeature.java
@RunWith(Cucumber.class)
@CucumberOptions(
  plugin = { "pretty", "org.citrusframework.cucumber.CitrusReporter" } )
public class MyFeatureIT {
}

The test case above uses the Cucumber JUnit test runner. In addition to that we give some options to the Cucumber execution. In case you want to have the usual Citrus test results reported you can add the special Citrus reporter implementation org.citrusframework.cucumber.CitrusReporter. This class is responsible for printing the Citrus test summary. This reporter extends the default Cucumber reporter so the default Cucumber report summary is also printed to the console.

That completes the JUnit class configuration. Now we are able to add feature stories and step definitions to the package of our test MyFeatureIT . Cucumber and Citrus will automatically pick up step definitions and glue code in that test package. So let’s write a feature story echo.feature right next to the MyFeatureIT test class.

echo.feature
Feature: Echo service

  Scenario: Say hello
    Given My name is Citrus
    When I say hello to the service
    Then the service should return: "Hello, my name is Citrus!"

  Scenario: Say goodbye
    Given My name is Citrus
    When I say goodbye to the service
    Then the service should return: "Goodbye from Citrus!"

As you can see this story defines two scenarios with the Gherkin Given-When-Then syntax. Now we need to add step definitions that glue the story description to Citrus test actions. Let’s do this in a new class EchoSteps .

EchoSteps.java
public class EchoSteps {

    @CitrusResource
    private TestCaseRunner runner;

    @Given("^My name is (.*)$")
    public void my_name_is(String name) {
        runner.variable("username", name);
    }

    @When("^I say hello.*$")
    public void say_hello() {
        runner.when(
                send("echoEndpoint")
                    .message()
                    .type(MessageType.PLAINTEXT)
                    .body("Hello, my name is ${username}!"));
    }

    @When("^I say goodbye.*$")
    public void say_goodbye() {
        runner.when(
                send("echoEndpoint")
                    .message()
                    .type(MessageType.PLAINTEXT)
                    .body("Goodbye from ${username}!"));
    }

    @Then("^the service should return: \"([^\"]*)\"$")
    public void verify_return(final String body) {
        runner.then(
                receive("echoEndpoint")
                    .message()
                    .type(MessageType.PLAINTEXT)
                    .body(body));
    }
}

The step definition class is a normal POJO that uses some annotations such as @CitrusResource annotated TestCaseRunner. The Citrus backend injects the test runner instance at runtime.

The step definition contains normal @Given, @When or @Then annotated methods that match the scenario descriptions in our feature file. Cucumber will automatically find matching methods and execute them. The methods add test actions to the test runner as we used to do in normal Java DSL tests.

That is a first combination of Citrus and Cucumber BDD. The feature file gets translated into step implementations that use Citrus test action runner Java API to run integration tests with behavior driven development.

4.4.2. Cucumber XML steps

The previous section handled glue code in Java in form of step definitions accessing the Java test runner fluent API. This chapter deals with the same concept with just XML configuration.

Citrus provides a separate configuration namespace and schema definition for Cucumber related step definitions. Include this namespace into your Spring configuration in order to use the Citrus Cucumber configuration elements.

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

    [...]

</spring:beans>

The JUnit Cucumber feature class itself does not change. We still use the Cucumber JUnit runner implementation with some options specific to Citrus:

MyFeatureIT.java
@RunWith(Cucumber.class)
@CucumberOptions(
    plugin = { "pretty", "org.citrusframework.cucumber.CitrusReporter" } )
public class MyFeatureIT {
}

The feature file with its Gherkin scenarios does also not change:

echo.feature
Feature: Echo service

  Scenario: Say hello
    Given My name is Citrus
    When I say hello to the service
    Then the service should return: "Hello, my name is Citrus!"

  Scenario: Say goodbye
    Given My name is Citrus
    When I say goodbye to the service
    Then the service should return: "Goodbye from Citrus!"

In the feature package my.company.features we add a new XML file EchoSteps.xml that holds the new XML step definitions:

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

  <step given="^My name is (.*)$" parameter-names="username">
    <citrus:create-variables>
      <citrus:variable name="username" value="${username}"/>
    </citrus:create-variables>
  </step>

  <step when="^I say hello.*$">
    <citrus:send endpoint="echoEndpoint">
      <citrus:message type="plaintext">
        <citrus:data>Hello, my name is ${username}!</citrus:data>
      </citrus:message>
    </citrus:send>
  </step>

  <step when="^I say goodbye.*$">
    <citrus:send endpoint="echoEndpoint">
      <citrus:message type="plaintext">
        <citrus:data>Goodbye from ${username}!</citrus:data>
      </citrus:message>
    </citrus:send>
  </step>

  <step then="^the service should return: &quot;([^&quot;]*)&quot;$" parameter-names="body">
    <citrus:receive endpoint="echoEndpoint">
      <citrus:message type="plaintext">
        <citrus:data>You just said: ${body}</citrus:data>
      </citrus:message>
    </citrus:receive>
  </step>

</spring:beans>

The above step definition uses pure XML actions. Citrus will automatically read the step definition and add those to the Cucumber runtime. Following from that the step definitions are executed when matching a statement in the feature story.

The XML step files follow a naming convention. Citrus will look for all files located in the feature package with name pattern **/**.Steps.xml and load those definitions when Cucumber starts up.

The XML steps are able to receive parameters from the Gherkin regexp matcher. The parameters are passed to the step as test variable. The parameter names get declared in the optional attribute parameter-names. In the step definitions you can use the parameter names as test variables.

The test variables are visible in all upcoming steps, too. This is because the test variables are global by default. If you need to set local state for a step definition you can use another attribute global-context and set it to false in the step definition. This way all test variables and parameters are only visible in the step definition. Other steps will not see the test variables.
Another notable thing is the XML escaping of reserved characters in the pattern definition. You can see that in the last step where the then attribute is escaping quotation characters.
Escape reserved characters
<step then="^the service should return: &quot;([^&quot;]*)&quot;$" parameter-names="body">
...
</step>

We have to do this because otherwise the quotation characters will interfere with the XML syntax in the attribute.

This completes the description of how to add XML step definitions to the cucumber BDD tests.

4.4.3. Cucumber Spring support

Cucumber provides support for Spring dependency injection in step definition classes. The Cucumber Spring capabilities are included in a separate module. So first of all we have to add this dependency to our project:

<dependency>
  <groupId>io.cucumber</groupId>
  <artifactId>cucumber-spring</artifactId>
  <version>${cucumber.version}</version>
</dependency>

The Citrus Cucumber extension has to handle things different when Cucumber Spring support is enabled. Therefore we use another object factory implementation that also support Cucumber Spring features. Change the object factory property in cucumber.properties to the following:

cucumber.properties
cucumber.object-factory=org.citrusframework.cucumber.backend.spring.CitrusSpringObjectFactory

Now we are ready to add @Autowired Spring bean dependency injection to step definition classes:

EchoSteps.java
@ContextConfiguration(classes = CitrusSpringConfig.class)
public class EchoSteps {
    @Autowired
    private Endpoint echoEndpoint;

    @CitrusResource
    protected TestDesigner designer;

    @Given("^My name is (.*)$")
    public void my_name_is(String name) {
        designer.variable("username", name);
    }

    @When("^I say hello.*$")
    public void say_hello() {
        designer.send(echoEndpoint)
            .messageType(MessageType.PLAINTEXT)
            .payload("Hello, my name is ${username}!");
    }

    @When("^I say goodbye.*$")
    public void say_goodbye() {
        designer.send(echoEndpoint)
            .messageType(MessageType.PLAINTEXT)
            .payload("Goodbye from ${username}!");
    }

    @Then("^the service should return: \"([^\"]*)\"$")
    public void verify_return(final String body) {
        designer.receive(echoEndpoint)
            .messageType(MessageType.PLAINTEXT)
            .payload("You just said: " + body);
    }
}

As you can see we used Spring autowiring mechanism for the echoEndpoint field in the step definition. Also be sure to define the @ContextConfiguration annotation on the step definition. The Cucumber Spring support loads the Spring application context and takes care of dependency injection. We use the Citrus CitrusSpringConfig Java configuration because this is the main entrance for Citrus test cases. You can add custom beans and further Spring related configuration to this Spring application context. If you want to add more beans for autowiring do so in the Citrus Spring configuration. Usually this is the default citrus-context.xml which is automatically loaded.

Of course, you can also use a custom Java Spring configuration class here. Please be sure to always import the Citrus Spring Java configuration classes, too.

As usual, we are able to use @CitrusResource annotated TestCaseRunner fields for building the Citrus integration test logic. With this extension you can use the full Spring testing power in your tests in particular dependency injection and also transaction management for data persistence tests.

4.4.4. YAKS step definitions

YAKS is a side project of Citrus and provides some predefined steps for typical integration test scenarios that you can use out-of-the-box.

You can basically define send/receive operations and many other predefined steps to handle Citrus test actions. As these steps are predefined in YAKS you just need to use them in your feature stories. The step definitions with glue to test actions is handled automatically in YAKS.

If you want to enable predefined steps support in your test you need to include the YAKS module as a Maven dependency.

YAKS module dependency
<dependency>
  <groupId>org.citrusframework.yaks</groupId>
  <artifactId>yaks-standard</artifactId>
  <version>${yaks.version}</version>
  <scope>test</scope>
</dependency>

After that you need to include the glue code package in your test class like this:

Include YAKS steps
@RunWith(Cucumber.class)
@CucumberOptions(
    extraGlue = { "org.citrusframework.yaks.standard" },
    plugin = { "pretty", "org.citrusframework.cucumber.CitrusReporter" } )
public class MyFeatureIT {

}

Instead of writing the glue code on our own in step definition classes we include the glue package org.citrusframework.yaks.standard as extra glue. This automatically loads all YAKS step definitions in this module. Once you have done this you can use predefined steps without having to write any glue code in Java.

The YAKS framework provides the following modules with predefined steps:

Table 1. YAKS modules
Module Description

yaks-standard

Standard steps such as test variables, sleep/delay, log/print, …​

yaks-http

Http steps for client and server side communication

yaks-openapi

Load Open API specifications and invoke/verify operations with generated test data

yaks-kubernetes

Manage Kubernetes resources (e.g. pods, deployments, custom resources)

yaks-knative

Steps to connect with Knative eventing and messaging

yaks-jms

Send/receive steps via JMS queues/topics

yaks-kafka

Steps to publish/subscribe on Kafka messaging

yaks-jdbc

Steps to connect to relational databases

yaks-camel

Steps to access Apache Camel components and Camel routes

yaks-camel-k

Manage Camel-K resources on Kubernetes

yaks-selenium

Run UI tests with Selenium using Selenium grid or standalone containers

yaks-groovy

Leverage Groovy scripts as Citrus endpoint and component configuration

Once again it should be said that the step definitions included in this modules can be used out-of-the-box. You can start to write feature stories in Gherkin syntax that trigger the predefined steps.

4.5. QuarkusTest runtime

Quarkus has emerged into a popular enterprise Java framework. For unit and integration testing the Quarkus framework provides a special integrations with JUnit Jupiter. Citrus adds a Quarkus test resource that developers can use to include Citrus capabilities into arbitrary Quarkus tests.

The Citrus QuarkusTest extension is shipped in a separate Maven module. You need to include the module as a dependency in your project accordingly.
Citrus Quarkus module dependency
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-quarkus</artifactId>
  <version>${citrus.version}</version>
</dependency>

Usually a Quarkus test is annotated with the @QuarkusTest or QuarkusIntegrationTest annotation. Users may add an annotation named @CitrusSupport in order to also enable Citrus capabilities on the test.

The Citrus support will automatically hook into the QuarkusTest lifecycle management making sure to call the Citrus before/after suite and before/after test handlers.

This way you are able to combine Citrus with @QuarkusTest annotated classes very easily.

Enable Citrus support on QuarkusTest
@QuarkusTest
@CitrusSupport
public class DemoApplicationTest {

    @CitrusFramework
    private Citrus citrus;

    @CitrusResource
    private TestCaseRunner t;

    @CitrusResource
    private TestContext context;

    @Test
    void shouldVerifyDemoApp() {
        t.when(
            send()
                .endpoint("messageEndpoint")
                .message()
                .body("How about Citrus!?")
        );

        t.when(
            receive()
                .endpoint("messageEndpoint")
                .message()
                .body("Citrus rocks!")
        );
    }
}

The @CitrusSupport annotation enables the Citrus features on the test. First of all users may inject Citrus related resources such as TestCaseRunner or TestContext.

The TestCaseRunner reference runs arbitrary Citrus actions as part of the test.

The test is also able to configure Message endpoints.

Configure message endpoints
@QuarkusTest
@CitrusSupport
public class DemoApplicationTest {

    @BindToRegistry
    private final KafkaEndpoint bookings = new KafkaEndpointBuilder()
            .topic("bookings")
            .build();

    @CitrusResource
    private TestCaseRunner t;

    @Test
    void shouldVerifyDemoApp() {
        t.when(
            send()
                .endpoint(bookings)
                .message()
                .body("How about Citrus!?")
        );

        t.when(
            receive()
                .endpoint(bookings)
                .message()
                .body("Citrus rocks!")
        );
    }
}

Creating new message endpoints is very easy. Just use the proper endpoint builder and optionally bind the new endpoint to the Citrus bean registry via BindToRegistry annotation.

You may then use the message endpoint in all send and receive test actions in order to exchange messages.

4.6. Main CLI runtime

Citrus provides a main class that you can run from command line.

The Main CLI support is shipped in a separate Maven module. You need to include the module as a dependency in your project.
Main CLI module dependency
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-main</artifactId>
  <version>${citrus.version}</version>
</dependency>
Run Citrus main

4.7. Sharding Test Cases

Sharding is a powerful option within the Citrus framework designed to facilitate the loading and execution of test cases in a distributed manner. Sharding test execution is expected to save roughly [TIME] * 1 / 2 of execution time per runner.

This chapter provides an overview of the available functionality and guides you through the process of configuring and using test loaders with sharding.

4.7.1. Configuration

First of all, you need to wrap your test case loading with the org.citrusframework.sharding.Shard#createShard(Stream<T> items) method. Have a look at the previous runtime chapters to get to know how to do this with the test framework of your choice.

Once you’re done doing so, the sharding can be configured using environment variables or system properties. The following two key configuration parameters are required for sharded test loading:

  • Total number of shards: Defines how many shards the test cases should be divided into.

  • Shard number: Specifies the specific shard number that this loader instance will handle.

The following table summarizes the full configuration options:

Configuration Environment Variable System Property

Total number of shards

CITRUS_SHARDING_TOTAL

citrus.sharding.total

Shard number

CITRUS_SHARDING_NUMBER

citrus.sharding.number

Shard seed

CITRUS_SHARDING_SEED

citrus.sharding.seed

When writing test cases in Java, additional constructors exist, such that the total number of shards, as well as the current shard number can be programmatically configured.

5. Running tests in Java

The Citrus test holds a sequence of test actions. Each action represents a very special purpose such as sending or receiving a message.

Despite the fact that message exchange is one of the main actions in an integration test framework for message-based applications Citrus is more than just that. Each test case in Citrus is able to perform various actions such as connecting to the database, transforming data, adding iterations and conditional steps.

With the default Citrus actions provided out of the box users can accomplish very complex use cases in integration testing. In Citrus you can configure and add test actions in Java using a test action runner API that leverages a fluent builder pattern API.

5.1. Test action runner

The test action runner is the entry to the fluent Java API. You can configure test actions with a fluent builder style API and immediately run the actions with the runner.

See the following example to see the Java domain specific language in action.

Test action runner
import org.testng.annotations.Test;
import org.citrusframework.annotations.CitrusTest;
import org.citrusframework.dsl.testng.TestNGCitrusSupport;

@Test
public class Service_IT extends TestNGCitrusSupport {

    @CitrusTest(name = "Service_IT")
    public void serviceTest() {
        run(echo("Before service call"));

        run(echo("After service call"));
    }
}

The test action runner executes each test action immediately as you use the provided run() method. All actions provided in Citrus represent a certain functionality (e.g. send/receive messages, delay the test, access a database). Citrus ships with a wide range of test actions, but you are also able to write your own test actions and execute them during a test.

By default, all actions run sequentially in the same order as they are defined in the test case. In case one single action fails the whole test case is failing. Of course, you can leverage parallel action execution with the usage of test containers.

The TestNGCitrusSupport and JUnit4CitrusSupport base classes are not thread safe by default. This is simply because the base class is holding state to the current test action runner instance in order to delegate method calls to this instance. Parallel test execution is not available with this approach. Fortunately there is a way to support parallel test execution through resource injection. Read more about this in JUnit4 or TestNG support.

5.2. Gherkin test action runner

The test action runner is also available as Gherkin style runner with given(), when(), then() methods. The Gherkin test action runner follows the Behavior Driven Development concepts of structuring the test into the three parts: Given a certain context, when an event occurs, then an outcome should be verified.

Gherkin test action runner
@Test
public class Service_IT extends TestNGCitrusSupport {

    @CitrusTest(name = "Service_IT")
    public void serviceTest() {
        given(
            echo("Setup the context")
        );

        when(
            echo("Trigger the event")
        );

        then(
            echo("Verify the outcome")
        );
    }
}

5.3. Test meta information

The user is able to 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.

Test meta information
@CitrusTest
public void sampleTest() {
    description("This is a Test");
    author("Christoph");
    status(Status.FINAL);

    run(echo("Hello Citrus!"));
}

The status allows the following values:

  • DRAFT

  • READY_FOR_REVIEW

  • DISABLED

  • FINAL

This information gives the reader first impression about the test and is also used to generate test documentation. By default, Citrus is able to generate test reports in HTML and Excel in order to 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.

The test description should give a short introduction to the intended use case scenario that will be tested. The user should get a short summary of what the test case is trying to verify.

5.4. Finally block

Java developers might be familiar with the concept of try-catch-finally blocks. 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.

Finally block
@CitrusTest
public void sampleTest() {
    given(
        doFinally()
            .actions(echo("Do finally - regardless of any error before"))
    );

    echo("Hello Test Framework");
}

As an example imagine that you have prepared some data inside the database at the beginning of the test and you need to make sure the data is cleaned up at the end of the test case.

Finally block example
@CitrusTest
public void finallyBlockTest() {
    variable("orderId", "citrus:randomNumber(5)");
    variable("date", "citrus:currentDate('dd.MM.yyyy')");

    given(
        doFinally()
            .actions(sql(dataSource).statement("DELETE FROM ORDERS WHERE ORDER_ID='${orderId}'"))
    );

    when(
        sql(dataSource).statement("INSERT INTO ORDERS VALUES (${orderId}, 1, 1, '${date}')")
    );

    then(
        echo("ORDER creation time: citrus:currentDate('dd.MM.yyyy')")
    );
}

In the example the first action creates an entry in the database using an INSERT statement. To be sure that the entry in the database is deleted after the test, the finally section contains the respective DELETE statement that is always executed regardless the test case state (successful or failed).

The finally section must be placed at the very beginning of the test. This is because the test action runner is immediately executing each test action as it is called within the Java DSL methods. This is the only way the test case can perform the final actions also in case of previous error.

A finally block placed at the very end of the test will not take action unless put in a traditional Java try-finally-block:

Traditional try-finally block
@CitrusTest
public void finallyBlockTest() {
    variable("orderId", "citrus:randomNumber(5)");
    variable("date", "citrus:currentDate('dd.MM.yyyy')");

    try {
        when(
            sql(dataSource).statement("INSERT INTO ORDERS VALUES (${orderId}, 1, 1, '${date}')")
        );

        then(
            echo("ORDER creation time: citrus:currentDate('dd.MM.yyyy')")
        );
    } finally {
        then(
            sql(dataSource).statement("DELETE FROM ORDERS WHERE ORDER_ID='${orderId}'")
        );
    }
}

Using the traditional Java try-finally feels more natural no doubt. Please notice that the Citrus report and logging will not account the traditional finally block actions then. Good news is whatever layout you choose the outcome is always the same.

The finally block is executed safely even in case some previous test action raises an error for some reason.

5.5. Test behaviors

The concept of test behaviors is a good way to reuse test action blocks in the Java DSL. Test behaviors combine action sequences to a logical unit. The behavior defines a set of test actions that can be applied multiple times to different test cases.

The behavior is a separate Java DSL class with a single apply method that configures the test actions. Test behaviors follow this basic interface:

Test behaviors
@FunctionalInterface
public interface TestBehavior {

    /**
     * Behavior building method.
     */
    void apply(TestActionRunner runner);

}

The behavior is provided with the test action runner and all actions in the behavior should run on that runner. Every time the behavior is applied to a test the actions get executed accordingly.

Test behaviors
public class FooBehavior implements TestBehavior {
    public void apply(TestActionRunner runner) {
        runner.run(createVariable("foo", "test"));

        runner.run(echo("fooBehavior"));
    }
}

public class BarBehavior implements TestBehavior {
    public void apply(TestActionRunner runner) {
        runner.run(createVariable("bar", "test"));

        runner.run(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 and defines test variables and actions as a normal test case would do. You can apply the behaviors multiple times in different tests:

@CitrusTest
public void behaviorTest() {
    run(apply(new FooBehavior()));

    run(echo("Successfully applied bar behavior"));

    run(apply(new BarBehavior()));

    run(echo("Successfully applied bar behavior"));
}

The behavior is applied to the test case by calling the apply() method. As a result the behavior is executed 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.

A behavior may use different variable names than 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 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).

5.6. Run custom code

In general, you are able to mix Citrus Java DSL actions with custom Java code as you like.

Run custom code
import org.testng.annotations.Test;
import org.citrusframework.annotations.CitrusTest;
import org.citrusframework.dsl.testng.TestNGCitrusSupport;

@Test
public class Service_IT extends TestNGCitrusSupport {

    private MyService myService = new MyService();

    @CitrusTest(name = "Service_IT")
    public void serviceTest() {
        run(echo("Before service call"));

        myService.doSomething("Now calling custom service");

        run(echo("After service call"));
    }
}

The test above uses a mix of Citrus test actions and custom service calls. The test logic will execute as expected. It is recommended though to wrap custom code in a test action in order to have a consistent test reporting and failure management in Citrus.

Test action wrapper
import org.testng.annotations.Test;
import org.citrusframework.annotations.CitrusTest;
import org.citrusframework.dsl.testng.TestNGCitrusSupport;

@Test
public class Service_IT extends TestNGCitrusSupport {

    private MyService myService = new MyService();

    @CitrusTest(name = "Service_IT")
    public void serviceTest() {
        run(echo("Before service call"));

        run(
            action(context -> {
                myService.doSomething("Now calling custom service");
            })
        );

        run(echo("After service call"));
    }
}

The sample above wraps the call to the custom service myService in an abstract test action represented as Java lambda expression. This way the service call becomes part of the Citrus test execution and failures are reported properly. Also you have access to the current test context which holds the list of test variables as well as many other Citrus related test objects (e.g. message store).

This is why you should wrap custom code in a test action and run that code via the test action runner methods. You can also put your custom code in a test action implementation and reference the logic from multiple tests.

5.7. Bind objects to registry

The Citrus context is a place where objects can register themselves in order to enable dependency injection and instance sharing in multiple tests. Once you register the object in the context others can resolve the reference with its given name.

In a simple example the context can register a new endpoint that is injected in several tests.

You can access the Citrus context within the provided before/after methods on the test.

Register endpoint in Citrus context
public class CitrusRegisterEndpoint_IT extends TestNGCitrusSupport {

    @Override
    public void beforeSuite(CitrusContext context) {
        context.bind("foo", new FooEndpoint());
    }
}

With the CitrusContext you can bind objects to the registry. Each binding receives a name so others can resolve the instance reference for injection.

Inject endpoint in other tests
public class InjectEndpoint_IT extends TestNGCitrusSupport {

    @CitrusEndpoint
    private FooEndpoint foo;

    @Test
    @CitrusTest
    public void injectEndpointTest() {
        $(send(foo)
                .message()
                .body("Hello foo!"));

        $(receive(foo)
                .message()
                .body("Hello Citrus!"));
    }
}

The @CitrusEndpoint annotation injects the endpoint resolving the instance with the given name foo. Test methods can use this endpoint in the following send and receive actions.

5.7.1. @BindToRegistry

An alternative to using the bind() method on the CitrusContext is to use the @BindToRegistry annotation. Methods and fields annotated will automatically register in the CitrusContext registry.

@BindToRegistry annotation
public class CitrusRegisterEndpoint_IT extends TestNGCitrusSupport {

    @CitrusFramework
    private Citrus citrus;

    @BindToRegistry(name = "fooQueue")
    private MessageQueue queue = new DefaultMessageQueue("fooQueue");

    @BindToRegistry
    public FooEndpoint foo() {
        return new FooEndpoint();
    }
}

The annotation is able to specify an explicit binding name. The annotation works with public methods and fields in tests.

5.7.2. Configuration classes

As an alternative to adding the registry binding configuration directly to the test you can load configuration classes.

Configuration classes are automatically loaded before a test suite run and all methods and fields are parsed for potential bindings. You can use the environment settings citrus.java.config and/or CITRUS_JAVA_CONFIG to set a default configuration class.

citrus-application.properties
citrus.java.config=MyConfig.class
MyConfig.class
public class MyConfig {

    @BindToRegistry(name = "fooQueue")
    private MessageQueue queue = new DefaultMessageQueue("fooQueue");

    @BindToRegistry
    public void foo() {
        return new FooEndpoint();
    }
}

5.7.3. @CitrusConfiguration

Each test is able to use the @CitrusConfiguration annotation to add registry bindings, too.

@CitrusConfiguration annotation
@CitrusConfiguration(classes = MyConfig.class)
public class CitrusRegisterEndpoint_IT extends TestNGCitrusSupport {

    @CitrusEndpoint
    private FooEndpoint foo;

    @Test
    @CitrusTest
    public void injectEndpointTest() {
        $(send(foo)
                .message()
                .body("Hello foo!"));

        $(receive(foo)
                .message()
                .body("Hello Citrus!"));
    }
}

The @CitrusConfiguration annotation is able to load configuration classes and bind all components to the registry for later usage. The test can inject endpoints and other components using the @CitrusEndpoint and @CitrusResource annotation on fields.

5.8. Resource injection

Resource injection is a convenient mechanism to access Citrus internal objects such as TestRunner or TestContext instances. The following sections deal with resource injection of different objects.

5.8.1. Inject Citrus framework

You can access the Citrus framework instance in order to access all components and functionalities. Just use the @CitrusFramework annotation in your test class.

Citrus framework injection
public class CitrusInjection_IT extends TestNGCitrusSupport {

    @CitrusFramework
    private Citrus citrus;

    @Test
    @CitrusTest
    public void injectCitrusTest() {
        citrus.getCitrusContext().getMessageListeners().addMessageListener(new MyListener());
    }
}

The framework instance provides access to the Citrus context which is a central registry for all components. The example above adds a new message listener.

The Citrus context is a shared component. Components added will perform with all further tests and changes made affect all tests.

5.8.2. Test action runner injection

The test action runner is the entry to the fluent Java API. You can inject the runner as a method parameter.

Test action runner injection
public class RunnerInjection_IT extends JUnit4CitrusSupport {

    @Test
    @CitrusTest
    public void injectResources(@CitrusResource TestCaseRunner runner) {

        runner.given(
            createVariable("random", "citrus:randomNumber(10)")
        );

        runner.run(
            echo("The random number is: ${random}")
        );
    }
}

The parameter requires the @CitrusResource annotations in order to mark the parameter for Citrus resource injection.

Now each method uses its own runner instance which makes sure that parallel test execution can take place without having the risk of side effects on other tests running at the same time.

5.8.3. Test context injection

The Citrus test context combines a set of central objects and functionalities that a test is able to make use of. The test context holds all variables and is able to resolve functions and validation matchers.

In general a tester will not have to explicitly access the test context because the framework is working with it behind the scenes. In terms of advanced operations and customizations accessing the test context may be a good idea though.

Each test action implementation has access to the test context as it is provided to the execution method in the interface:

Test action interface
@FunctionalInterface
public interface TestAction {
    /**
     * Main execution method doing all work
     * @param context
     */
    void execute(TestContext context);
}

In addition Citrus provides a resource injection mechanism that allows to access the current test context in a test class or test method.

Inject as method parameter
public class TestContextInjection_IT extends JUnit4CitrusSupport {

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

        run(echo("${myVariable}"));
    }
}

As you can see the test method defines a parameter of type org.citrusframework.context.TestContext. The annotation @CitrusResource tells Citrus to inject this parameter with the according instance of the context for this test.

Now you have access to the context and all its capabilities such as variable management. As an alternative you can inject the test context as a class member variable.

Inject as member
public class TestContextInjection_IT extends JUnit4CitrusSupport {

    @CitrusResource
    private TestContext context;

    @Test
    @CitrusTest
    public void resourceInjectionIT() {
        context.setVariable("myVariable", "some value");

        run(echo("${myVariable}"));
    }
}

5.8.4. Endpoint injection

Endpoints play a significant role when sending/receiving messages over various transports. An endpoint defines how to connect to a message transport (e.g. Http endpoint URL, JMS message broker connection, Kafka connection and topic selection).

Endpoints can live inside the Citrus context (e.g. in Spring application context) or you can inject the endpoint into the test class with given configuration.

Endpoint injection
public class EndpointInjectionJavaIT extends TestNGCitrusSpringSupport {

    @CitrusEndpoint
    @DirectEndpointConfig(queueName = "FOO.test.queue")
    private Endpoint directEndpoint;

    @Test
    @CitrusTest
    public void injectEndpoint() {
        run(send(directEndpoint)
                .message()
                .type(MessageType.PLAINTEXT)
                .body("Hello!"));

        run(receive(directEndpoint)
                .message()
                    .type(MessageType.PLAINTEXT)
                    .body("Hello!"));
    }
}

The sample above creates a new endpoint as a direct in-memory channel endpoint. Citrus reads the @CitrusEndpoint annotation and adds the configuration as given in the @DirectEndpointConfig annotation. This way you can create and inject endpoints directly to your test.

Citrus also supports the Spring framework as a central bean registry. You can add endpoints as Spring beans and use the @Autowired annotation to inject the endpoint in your test.

6. Test variables

The usage of test variables is a core concept when writing maintainable tests. The key identifiers of a test case should be exposed as test variables at the very beginning of a test. This avoids hard coded identifiers and multiple redundant values inside the test.

Java
public void fooService_IT() {
    variable("text", "Hello Citrus!");
    variable("customerId", "123456789");

    run(echo("Text: ${text} Id: ${id}"));
}
XML
<testcase name="FooService_IT">
  <variables>
    <variable name="text" value="Hello Citrus!"/>
    <variable name="customerId" value="123456789"/>
  </variables>

  <actions>
    <echo>
      <message>Text: ${text} Id: ${id}</message>
    </echo>
  </actions>
</testcase>

Test variables help significantly when writing complex tests with lots of identifiers and semantic data. The variables are valid for the whole test case. You can reference a variable multiple times using a common variable expression ${variable-name}.

The usage of variables should make 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 (e.g. different error codes, identifiers).

The name of the variable is arbitrary. Of course, you need to be careful with special characters and reserved XML entities like '&', '<', '>'. In general, you can apply to the Java naming convention, and you will be fine.

6.1. Global variables

You can work with different variable scopes (local or global). Local variables are accessible throughout a single test. Global variables are visible for all tests yet global variables are immutable, so tests cannot change their values.

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 Citrus context. The following example demonstrates how to add global variables in Citrus:

Java DSL
@Bean
public GlobalVariables globalVariables() {
    return new GlobalVariables.Builder()
        .variable("projectName", "Citrus Integration Testing")
        .variable("userName", "TestUser")
        .build();
}
XML DSL
<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:

Java DSL
@Bean
public GlobalVariablesPropertyLoader propertyLoader() {
    GlobalVariablesPropertyLoader propertyLoader = new GlobalVariablesPropertyLoader();

    propertyLoader.getPropertyFiles().add("classpath:global-variable.properties");

    return propertyLoader;
}
XML DSL
<citrus:global-variables>
  <citrus:file path="classpath:global-variable.properties"/>
</citrus:global-variables>

You can use the GlobalVariablesPropertyLoader component and add it to the context as a Spring bean. Citrus loads the given 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:
global-variable.properties
user=Citrus
greeting=Hello ${user}!
date=citrus:currentDate('yyyy-MM-dd')

6.2. Extract variables

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. In many cases you might want to save this identifier in order to use this value in the respective response message or somewhat later on in the test.

The solution is to extract dynamic values from received messages and save those to test variables at runtime.

6.2.1. JsonPath expressions

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.

<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.

6.2.2. XPath expressions

Add this code to your message receiving action.

Java DSL
@CitrusTest
public void receiveMessageTest() {
    when(
        receive("helloService")
            .extract(fromBody()
                    .expression("//TestRequest/VersionId", "versionId"))
            .extract(fromHeaders()
                    .header("Operation", "operation"))
    );

    then(
        echo("Extracted operation from header is: ${operation}")
    );

    then(
        echo("Extracted version from body is: ${versionId}")
    );
}
XML DSL
<receive endpoint="helloService">
    <message>
      ...
    </message>
    <extract>
      <header name="Operation" variable="operation"/>
      <message path="/TestRequest/VersionId" variable="versionId"/>
    </extract>
</receive>

<echo>
  <message>Extracted operation from header is: ${operation}</message>
</echo>

<echo>
  <message>Extracted version from body is: ${versionId}</message>
</echo>

As you can see Citrus is able to extract both header and message body content into test variables. 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.

6.3. Path expressions

Some elements in message body might be of dynamic nature. Just think of generated identifiers or timestamps. This is the right time to use test variables and dynamic message element overwrite. You can overwrite specific elements in the message body with path expressions (XPath or JsonPath).

6.3.1. JsonPath expressions

First thing we want to do with JsonPath is to manipulate a message content before it is actually processed. 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 processing the message content.

Let’s have a look at this simple sample Json message body:

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

Citrus can load the file content and uses it as message body when sending or receiving messages in a test case. You can apply JsonPath expressions in order to manipulate the message content.

<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>

When all path expressions are evaluated 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 set the username to Admin . The admin boolean property was set to true and all project status values were set to closed. 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 you 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.

6.3.2. XPath expressions

In case of XML message bodies you can use XPath expressions to manipulate the body content before any message processing takes place.

XML DSL
<message>
  <payload>
    <TestMessage>
      <MessageId>${messageId}</MessageId>
      <CreatedAt>?</CreatedAt>
      <VersionId>${version}</VersionId>
    </TestMessage>
  </payload>
  <element path="/TestMessage/CreatedAt" value="${date}"/>
</message>

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

The sample above uses an XPath expression that evaluates and searches for the right element in the message body in order to set the given value. The previously defined variable ${date} replaces the respective element value. Of course this works with XML attributes too (e.g. path expression /TestMessage/Person/@age).

Both ways via XPath or JsonPath 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 you can overwrite values in message templates in order to increase maintainability and robustness of your test.

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.

6.4. Escape variables

The test variable expression syntax ${variable-name} is preserved to evaluate to a test variable within the current test context. In case the same syntax is used in one of your message content values you need to escape the syntax from being interpreted as test variable expression. You can do this by using the variable expression escaping character sequence // wrapping the actual variable name like this:

Plain text message content with escapes
This is an 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 body.

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.

Parsed plain text mesage content
This is an escaped variable expression ${escaped} and should not lead to unknown variable exceptions within Citrus.

7. Message validation

When Citrus receives a message from external applications it is time to verify the message content. This message validation includes syntax rules with schema validation and message content comparison to expected templates. Citrus provides powerful message validation capabilities for different data formats. The tester is able to define expected message headers and body content. The Citrus message validator finds values not matching the expectations and reports the difference as test failure.

7.1. Validation registry

Citrus provides default message validator implementations for different data formats. The Citrus project context automatically loads these default message validators. In case one of these message validators matches the incoming message the message validator performs its validation steps with the message.

All default message validators can be overwritten by binding a component with the same id to the project context (e.g. as Spring bean in the application context).

The default message validator implementations of Citrus are:

defaultXmlMessageValidator

org.citrusframework.validation.xml.DomXmlMessageValidator

defaultXpathMessageValidator

org.citrusframework.validation.xml.XpathMessageValidator

defaultJsonMessageValidator

org.citrusframework.validation.json.JsonTextMessageValidator

defaultJsonPathMessageValidator

org.citrusframework.validation.json.JsonPathMessageValidator

defaultPlaintextMessageValidator

org.citrusframework.validation.text.PlainTextMessageValidator

defaultMessageHeaderValidator

org.citrusframework.validation.DefaultMessageHeaderValidator

defaultBinaryBase64MessageValidator

org.citrusframework.validation.text.BinaryBase64MessageValidator

defaultGzipBinaryBase64MessageValidator

org.citrusframework.validation.text.GzipBinaryBase64MessageValidator

defaultXhtmlMessageValidator

org.citrusframework.validation.xhtml.XhtmlMessageValidator

defaultGroovyXmlMessageValidator

org.citrusframework.validation.script.GroovyXmlMessageValidator

defaultGroovyTextMessageValidator

org.citrusframework.validation.script.GroovyScriptMessageValidator

defaultGroovyJsonMessageValidator

org.citrusframework.validation.script.GroovyJsonMessageValidator

You can overwrite a default message validator with a custom implementation. Just add your customized validator implementation as a bean to the Citrus context and use one of the default bean identifiers.

You can add a custom message validator as a component in the context (e.g. as Spring bean in the application context).

Java DSL
@Bean
public CustomMessageValidator customMessageValidator() {
    return new CustomMessageValidator();
}
XML DSL
<bean id="customMessageValidator" class="org.citrusframework.validation.custom.CustomMessageValidator"/>

The listing above adds a custom message validator implementation. The message validator registry will automatically add this validator to the list of available validators in the project.

The custom implementation class has to implement the basic interface org.citrusframework.validation.MessageValidator<>. Now Citrus will try to match the custom implementation to incoming message types and occasionally execute the message validator logic when applicable.

7.2. Validation modules

The list of available message validators in your project is controlled by the available message validator implementations on the project classpath.

You need to add validator modules to the project accordingly. For instance if you want to use the default Json message validation capabilities in Citrus you need to add the following dependency:

Json validation module dependency
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-validation-json</artifactId>
  <version>${citrus.version}</version>
</dependency>

This adds the Citrus message validator component for dealing with Json message format. This includes message validators and JsonPath support. Now your Citrus project is able to validate Json messages.

Citrus provides the following validation modules:

Read more about the individual validation modules in the next sections.

7.3. Json validation

Message formats such as Json have become very popular, in particular when dealing with RESTful services. Citrus is able to expect and validate Json messages with a powerful comparison of Json structures.

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.

Json message validation is not enabled by default in your project. You need to add the validation module to your project as a Maven dependency.

Json validation module dependency
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-validation-json</artifactId>
  <version>${citrus.version}</version>
</dependency>

Citrus provides several default message validator implementations for Json messages:

JsonTextMessageValidator

Basic Json message validator implementation compares Json objects/arrays (expected and received).

  • The order of Json object properties can differ, whereas array order is always validated.

  • Test variables and ignored-placeholders (@ignore@) can be used.

  • JsonArray as well as nested JsonObjects are supported.

GroovyJsonMessageValidator

Extended groovy message validator provides specific Json slurper support. With Json slurper the tester can validate the Json message body 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 entries (object properties / array items) to match in received and control message. No additional fields in received Json data structure will be accepted. In soft mode the validator allows additional entries (object properties / array items) in received Json, so the control Json can be a subset of the recieved. Additional entries in the received Json data structure are ignored.
The Json validation mode (strict or soft) is settable via environment variable CITRUS_JSON_MESSAGE_VALIDATION_STRICT or system property citrus.json.message.validation.strict=false. This will set soft mode to all Json text message validators.

You can also overwrite these 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:

Java
@Bean
public JsonTextMessageValidator defaultJsonMessageValidator() {
    return new JsonTextMessageValidator();
}
XML
<bean id="defaultJsonMessageValidator" class="org.citrusframework.validation.json.JsonTextMessageValidator"/>

The same approach applies to the Groovy message validator implementation.

Java
@Bean
public GroovyJsonMessageValidator defaultGroovyJsonMessageValidator() {
    return new GroovyJsonMessageValidator();
}
XML
<bean id="defaultGroovyJsonMessageValidator" class="org.citrusframework.validation.script.GroovyJsonMessageValidator"/>

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

When a message has been received in Citrus the message validation will try to find a matching message validator according to the message content. You can also specify the Json message format on a receive action in order to force Json message validation.

Java
receive(someEndpoint)
    .message()
    .type(MessageType.JSON)
    .body("{" +
            "\"type\" : \"read\"," +
            "\"mbean\" : \"java.lang:type=Memory\"," +
            "\"attribute\" : \"HeapMemoryUsage\"," +
            "\"path\" : \"@equalsIgnoreCase('USED')@\"," +
            "\"value\" : \"${heapUsage}\"," +
            "\"timestamp\" : \"@ignore@\"" +
          "}");
XML
<receive endpoint="someEndpoint">
    <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.

Instead of defining an expected message body template we can also use Groovy validation scripts. Let’s 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.

Java
receive(someEndpoint)
    .message()
    .type(MessageType.JSON)
    .validate(groovy()
        .script("assert json.type == 'read'\n" +
                "assert json.mbean == 'java.lang:type=Memory'\n" +
                "assert json.attribute == 'HeapMemoryUsage'\n" +
                "assert json.value == '${heapUsage}'"));
XML
<receive endpoint="someEndpoint">
    <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 the message type tells Citrus that we expect a message of type json. The action uses a validation script written in Groovy to verify the incoming message. Citrus will automatically activate the special message validator that executes our Groovy script.

The script validation is very 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 injected in 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 body or header.

Java
receive(someEndpoint)
    .message()
    .type(MessageType.JSON)
    .validate(groovy()
        .script("assert receivedMessage.getPayload(String.class).contains(\"Hello Citrus!\")\n" +
                "assert receivedMessage.getHeader(\"Operation\") == 'sayHello'\n" +
                "context.setVariable(\"request_body\", receivedMessage.getPayload(String.class))"));
XML
<receive endpoint="someEndpoint">
    <message type="json">
        <validate>
            <script type="groovy">
                assert receivedMessage.getPayload(String.class).contains("Hello Citrus!")
                assert receivedMessage.getHeader("Operation") == 'sayHello'

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

The listing above shows some power of the validation script. We can access the message body, we can access the message header. With test context access we can also save the whole message body 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. Neither you do have code syntax assist nor code completion when using inline Groovy scripts.

Also, in case the validation script gets more complex you might want to load the script from an external file resource.

Java
receive(someEndpoint)
    .message()
    .type(MessageType.JSON)
    .validate(groovy()
        .script(new ClassPathResource("path/to/validationScript.groovy")));
XML
<receive endpoint="someEndpoint">
    <message type="json">
        <validate>
            <script type="groovy" file="path/to/validationScript.groovy"/>
        </validate>
    </message>
</receive>

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. This means that multiple message validators can coexist in a Citrus project.

Multiple message validators that all apply to the message content format will run in sequence. In case you need to explicitly choose a message validator implementation you can do so in the receive action:

Java
receive(someEndpoint)
    .validator(groovyJsonMessageValidator)
    .message()
    .type(MessageType.JSON)
    .validate(groovy()
        .script(new ClassPathResource("path/to/validationScript.groovy")));
XML
<receive endpoint="someEndpoint">
    <message type="json" validator="groovyJsonMessageValidator">
        <validate>
            <script type="groovy" file="path/to/validationScript.groovy"/>
        </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 consolidates all available message validators. 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.)

7.3.1. 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. You can use a JsonPath expression for ignoring a very specific entry in the Json object structure.

Java
receive(someEndpoint)
    .message()
    .type(MessageType.JSON)
    .body("{\"users\":" +
            "[{" +
                "\"name\": \"Jane\"," +
                "\"token\": \"?\"," +
                "\"lastLogin\": 0" +
            "}," +
            "{" +
                "\"name\": \"Penny\"," +
                "\"token\": \"?\"," +
                "\"lastLogin\": 0" +
            "}," +
            "{" +
                "\"name\": \"Mary\"," +
                "\"token\": \"?\"," +
                "\"lastLogin\": 0" +
            "}]" +
        "}")
    .validate(json()
                .ignore("$.users[*].token")
                .ignore("$..lastLogin"));
XML
<receive endpoint="someEndpoint">
    <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>
</receive>

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.

7.3.2. JsonPath validation

Let’s continue to use JsonPath expressions when validating a received message in Citrus:

Java
receive(someEndpoint)
    .message()
    .type(MessageType.JSON)
    .validate(jsonPath()
        .expression("$.user.name", "Penny")
        .expression("$['user']['name']", "${userName}")
        .expression("$.user.aliases", "[\"penny\",\"jenny\",\"nanny\"]")
        .expression("$.user[?(@.admin)].password", "@startsWith('$%00')@")
        .expression("$.user.address[?(@.type='office')]", "{\"city\":\"Munich\",\"street\":\"Company Street\",\"type\":\"office\"}"));
XML
<receive endpoint="someEndpoint">
  <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="{&quot;city&quot;:&quot;Munich&quot;,&quot;street&quot;:&quot;Company Street&quot;,&quot;type&quot;:&quot;office&quot;}"/>
    </validate>
  </message>
</receive>
Use path expression map
final Map<String, Object> validationMap = new HashMap<>();
validationMap.put("$.user.name", "Penny");
validationMap.put("$['user']['name']", "${userName}");
validationMap.put("$.user.aliases", "[\"penny\",\"jenny\",\"nanny\"]");
validationMap.put("$.user[?(@.admin)].password", "@startsWith('$%00')@");
validationMap.put("$.user.address[?(@.type='office')]", "{\"city\":\"Munich\",\"street\":\"Company Street\",\"type\":\"office\"}");

receive(someEndpoint)
    .message()
    .type(MessageType.JSON)
    .validate(jsonPath().expressions(validationMap));

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 let’s 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:

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

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

Now let’s 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:

Java
receive(someEndpoint)
    .message()
    .type(MessageType.JSON)
    .validate(jsonPath()
                .expression("$.user.keySet()", contains("id","name","admin","projects"))
                .expression("$.user.aliases.size()", allOf(greaterThan(0), lessThan(5))));
XML
<receive endpoint="someEndpoint">
  <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>
</receive>

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.

7.3.3. 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 within every receive-message action by setting schema-validation="true"
Java
receive(someEndpoint)
    .message()
    .type(MessageType.JSON)
    .body()
    .validate(json()
        .schemaValidation(true)
        .schema("bookStore"));
XML
<receive endpoint="echoHttpServer">
  <message type="json" schema="bookStore" schema-validation="true">
    <data>
      {
        "isbn" : "0345391802",
        "title": "The Hitchhiker's Guide to the Galaxy",
        "author": "Douglas Adams"
      }
    </data>
  </message>
</receive>

Json schema validation in Citrus is optional and disabled by default. This is why the action required to explicitly enable the schema validation with schemaValidation(true). The schema references a bean in the Citrus context (e.g. a Spring bean in the application context). Read more about how to declare schemas in schema validation.

We encourage you to add Json schema validation to your test cases as soon as possible, because we think that message validation is an important part of integration testing.

7.3.4. 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.

Java
@Bean
public JsonSchemaRepository schemaRepository() {
    JsonSchemaRepository repository = new JsonSchemaRepository();
    repository.getSchemas().add(productSchema());
    return repository;
}
XML
<citrus:schema-repository type="json" id="jsonSchemaRepository">
    <citrus:schemas>
        <citrus:schema ref="productSchema" location="classpath:org/citrusframework/validation/ProductsSchema.json"/>
    </citrus:schemas>
</citrus:schema-repository>

The referenced schema is another bean in the configuration that represents the schema definition.

Java
@Bean
public SimpleJsonSchema productSchema() {
    return new SimpleJsonSchema(
            new ClassPathResource("classpath:org/citrusframework/validation/ProductsSchema.json"));
}
XML
<citrus:schema id="productSchema" location="classpath:org/citrusframework/validation/ProductsSchema.json"/>

7.3.5. 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 within 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 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.

7.4. XML 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 and supports XML namespaces, attributes and XML schema validation.

XML message validation is not enabled by default in your project. You need to add the validation module to your project as a Maven dependency.

XML validation module dependency
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-validation-xml</artifactId>
  <version>${citrus.version}</version>
</dependency>

The default XML message validator implementation is active by default and can be overwritten with a custom implementation using the bean id defaultXmlMessageValidator .

Java
@Bean
public DomXmlMessageValidator defaultXmlMessageValidator() {
    return new DomXmlMessageValidator();
}
XML
<bean id="defaultXmlMessageValidator" class="org.citrusframework.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 as well as namespace qualified attributes. See the following sections for a detailed description of all capabilities.

7.4.1. XML payload validation

Citrus is able to verify XML message content on a received message. You as a tester can compare the whole XML message body to a predefined control message template. The Citrus message validator will walk through the XML document and compare the elements, attributes and values.

You can define the expected XML message template in multiple ways:

  • Defines the message body as nested XML message template in the test code. The whole message body is defined inside the test case.

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

In Java tests that use the Citrus domain specific language you must use verbose String concatenation when constructing XML message contents inline. You need to escape reserved characters like quotes and line breaks. This is why you should consider to using external file resources in Java when dealing with large complex message data.

Java
@CitrusTest
public void externalBodyResourceTest() {
    receive("someEndpoint")
        .message()
        .body(new ClassPathResource("org/citrusframework/message/data/TestRequest.xml"))
        .header("Operation", "sayHello")
        .header("MessageId", "${messageId}");
}
XML
<receive endpoint="someEndpoint">
  <message>
    <payload file="classpath:org/citrusframework/message/data/TestRequest.xml"/>
  </message>
</receive>

Inline message body definition or external file resource give us a control message template that the test case expects to validate. 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 bodies Citrus will navigate through the whole XML structure validating each element and its content.

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

Citrus supports various ways to add dynamic message content in the message template. Secondly, Citrus can ignore some elements that should not be 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.

7.4.2. XML header validation

A message can have multiple headers in addition to the body content. You can validate headers with expected name and the control value. Just add the following header validation to your receiving action.

Java
receive("someEndpoint")
    .message()
    .header("Operation", "sayHello")
    .header("MessageId", "${messageId}");
XML
<receive endpoint="someEndpoint">
  <message>
    <header>
        <element name="Operation" value="GetCustomer"/>
        <element name="RequestTag" value="${requestTag}"/>
    </header>
  </message>
</receive>

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 available, or the value does not match the expectations the test raises validation errors.

7.4.3. XML header fragment validation

Sometimes message headers may not apply to the name-value pair pattern. For example SOAP headers can also contain XML fragments as header values. You can add complex header data as expected value in the validation.

Java
receive("someEndpoint")
    .message()
    .header("Operation", "SayHello")
    .header("<ns0:HelloHeader xmlns:ns0=\"http://citrusframework.org/schemas/samples/HelloService.xsd\">" +
                "<ns0:MessageId>${messageId}</ns0:MessageId>" +
                "<ns0:CorrelationId>${correlationId}</ns0:CorrelationId>" +
                "<ns0:User>${user}</ns0:User>" +
                "<ns0:Text>Hello from Citrus!</ns0:Text>" +
            "</ns0:HelloHeader>");
XML
<receive endpoint="someEndpoint">
  <message>
    <header>
      <data>
        <![CDATA[
          <ns0:HelloHeader xmlns:ns0="http://citrusframework.org/schemas/samples/HelloService.xsd">
              <ns0:MessageId>${messageId}</ns0:MessageId>
              <ns0:CorrelationId>${correlationId}</ns0:CorrelationId>
              <ns0:User>${user}</ns0:User>
              <ns0:Text>Hello from Citrus!</ns0:Text>
          </ns0:HelloHeader>
        ]]>
      </data>
      <element name="Operation" value="SayHello"/>
    </header>
  </message>
</receive>

The header data has no name but uses a complex XML fragment as a value. In SOAP this header fragment will be added as a SOAP-ENV:Header then. Please read more about this in SOAP support.

7.4.4. Ignore XML elements

Some elements in the message payload might not apply for validation at all. Just think of communication timestamps or dynamic values that have been generated from a foreign service.

You as a tester may not be able to predict such a timestamp or dynamic value for expected validation. This is why you can safely ignore elements and attributes in the XML message validation.

Java
receive("someEndpoint")
    .message()
    .header("<TestMessage>" +
                "<VersionId>${versionId}</VersionId>" +
                "<Timestamp>?</Timestamp>" +
                "<MessageId>?</MessageId>" +
            "</TestMessage>")
    .validate(xpath()
            .ignore("/TestMessage/Timestamp")
            .ignore("/TestMessage/MessageId"));
XML
<receive endpoint="someEndpoint">
  <message>
    <payload>
      <TestMessage>
        <VersionId>${versionId}</VersionId>
        <Timestamp>?</Timestamp>
        <MessageId>?</MessageId>
      </TestMessage>
    </payload>
    <ignore path="/TestMessage/Timestamp"/>
    <ignore path="/TestMessage/MessageId"/>
  </message>
</receive>

The receive action above is not able to verify the elements Timestamp and MessageId. This is because the timestamp uses milliseconds and the message id has been generated by the server application. Both values must be excluded from XML validation.

You can use ignore XPath expressions that match elements in the message content that should be excluded. XPath expressions can be cumbersome and error prone though.

You can also use inline @ignore@ expressions as expected template values in order to exclude elements from valdidation. This is for those of you that do not like to write XPath expressions. As a result the ignored message elements are automatically skipped when Citrus compares and validates message contents and do not break the test case.

Java
receive("someEndpoint")
    .message()
    .header("<TestMessage>" +
                "<VersionId>${versionId}</VersionId>" +
                "<Timestamp>@ignore@</Timestamp>" +
                "<MessageId>@ignore@</MessageId>" +
            "</TestMessage>");
XML
<receive endpoint="someEndpoint">
  <message>
    <payload>
      <TestMessage>
        <VersionId>${versionId}</VersionId>
        <Timestamp>@ignore@</Timestamp>
        <MessageId>@ignore@</MessageId>
      </TestMessage>
    </payload>
  </message>
</receive>

Feel free to mix both mechanisms to ignore message elements. Ignore expressions are valid as elements, sub-tree nodes and attributes. You can use the @ignore@ placeholder in external file resources, too.

7.4.5. XPath validation

The section XML payload validation showed how to validate the complete XML message structure with control message template. 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. You would rather want to use explicit element validation with XPath.

Java
receive("someEndpoint")
    .message()
    .validate(xpath()
        .expression("/TestRequest/MessageId", "${messageId}")
        .expression("//VersionId", "2"));
}
XML
<receive endpoint="someEndpoint">
  <message>
    <validate>
      <xpath expression="/TestRequest/MessageId" value="${messageId}"/>
      <xpath expression="/TestRequest/VersionId" value="2"/>
    </validate>
  </message>
</receive>

In Java the use of a map may be the easiest way to declare multiple expressions for XPath validation.

Java DSL
final Map<String, Object> expressions = new HashMap<>();
expressions.put("/TestRequest/MessageId", "${messageId}");
expressions.put("//VersionId", "2");

receive("someEndpoint")
    .message()
    .validate(xpath()
        .expressions(expressions));
}

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 an 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. 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 .

Java
receive("someEndpoint")
    .validate(xpath()
        .expression("boolean:/TestRequest/Error", false)
        .expression("number:/TestRequest/Status[.='success']", 3)
        .expression("node-set:/TestRequest/OrderType", "[single, multi, multi]");
XML
<receive endpoint="someEndpoint">
  <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>
</receive>

In the example above we use different expression result types. First we want to make sure no /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 but 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.

You can use even more powerful validation expressions with matcher implementations. With validation matchers you are able to use validations such as greaterThan, lessThan, hasSize and much more.

Java
receive("someEndpoint")
    .validate(xpath()
        .expression("/TestRequest/Error", anyOf(empty(), nullValue()))
        .expression("number:/TestRequest/Status[.='success']", greaterThan(0.0))
        .expression("integer:/TestRequest/Status[.='failed']", lowerThan(1))
        .expression("node-set:/TestRequest/OrderType", hasSize(3));
XML
<receive endpoint="someEndpoint">
  <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>
</receive>
The validation matchers used in the example above use the citrus-hamcrest-validation module.
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 an 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.

7.4.6. XML namespaces

Namespaces represent an essential concept in XML. A namespace declares an element to be part of a very specific ruleset. You have to specify namespaces also when using XPath expressions. Let’s have a look at an example message that uses XML namespaces:

Sample XML body with namepaces
<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

Java
receive("someEndpoint")
    .validate(xpath()
        .expression("//TestMessage/TestHeader/VersionId", 2L)
        .expression("//TestMessage/TestHeader/CorrelationId", "${correlationId}");
XML
<receive endpoint="someEndpoint">
  <message>
    <validate>
      <xpath expression="//TestMessage/TestHeader/VersionId" value="2"/>
      <xpath expression="//TestMessage/TestHeader/CorrelationId" value="${correlationId}"/>
    </validate>
  </message>
</receive>

The validation will fail although the XPath expression looks correct regarding the XML tree. This is because the message uses the namespace xmlns:ns1="http://citrus.com/namespace". The XPath expression is not able to find the elements because of the missing namespace declaration in the expression. The correct XPath expression uses the namespace prefix as defined in the message.

Java
receive("someEndpoint")
    .validate(xpath()
        .expression("//ns1:TestMessage/ns1:TestHeader/ns1:VersionId", 2L)
        .expression("//ns1:TestMessage/ns1:TestHeader/ns1:CorrelationId", "${correlationId}");
XML
<receive endpoint="someEndpoint">
  <message>
    <validate>
      <xpath expression="//ns1:TestMessage/ns1:TestHeader/ns1:VersionId" value="2"/>
      <xpath expression="//ns1:TestMessage/ns1:TestHeader/ns1:CorrelationId" value="${correlationId}"/>
    </validate>
  </message>
</receive>

Now the expressions works fine, and the validation is successful. Relying on the namespace prefix ns1 is quite error-prone though. This is because the test depends on the very specific namespace prefix. 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.

Java
receive("someEndpoint")
    .validate(xpath()
        .expression("//pfx:TestMessage/pfx:TestHeader/pfx:VersionId", 2L)
        .expression("//pfx:TestMessage/pfx:TestHeader/pfx:CorrelationId", "${correlationId}")
        .namespaceContext("pfx", "http://citrus.com/namespace"));
XML
<receive endpoint="someEndpoint">
  <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>
</receive>

Now the test is independent of 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.
Java
@Bean
public NamespaceContextBuilder namespaceContext() {
    NamespaceContextBuilder builder = new NamespaceContextBuilder();
    builder.getNamepspaceMappings().put("pfx", "http://citrusframework.org/samples/sayHello");
    return builder;
}
XML
<namespace-context>
    <namespace prefix="def" uri="http://citrusframework.org/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).

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

Sample XML body 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.

Java
receive("someEndpoint")
    .validate(xpath()
        .expression("//TestMessage/TestHeader/VersionId", 2L)
        .expression("//TestMessage/TestHeader/CorrelationId", "${correlationId}"));
XML
<receive endpoint="someEndpoint">
  <message>
    <validate>
      <xpath expression="//TestMessage/TestHeader/VersionId" value="2"/>
      <xpath expression="//TestMessage/TestHeader/CorrelationId" value="${correlationId}"/>
    </validate>
  </message>
</receive>

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

Java
receive("someEndpoint")
    .validate(xpath()
        .expression("//:TestMessage/:TestHeader/:VersionId", 2L)
        .expression("//:TestMessage/:TestHeader/:CorrelationId", "${correlationId}"));
XML
<receive endpoint="someEndpoint">
  <message>
    <validate>
      <xpath expression="//:TestMessage/:TestHeader/:VersionId" value="2"/>
      <xpath expression="//:TestMessage/:TestHeader/:CorrelationId" value="${correlationId}"/>
    </validate>
  </message>
</receive>
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.

7.4.7. 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. 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 an XmlConfigurer Spring bean to the Citrus application context.

Java
@Bean
public XmlConfigurer xmlConfigurer() {
    XmlConfigurer configurer = new XmlConfigurer();
    configurer.getParseSettings().put("validate-if-schema", false);

    configurer.getSerializeSettings().put("comments", false);
    configurer.getSerializeSettings().put("format-pretty-print", false);
    return configurer;
}
XML
<bean id="xmlConfigurer" class="org.citrusframework.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.

7.4.8. 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:

Java
receive("someEndpoint")
    .validate(groovy().script("assert root.children().size() == 4\n" +
                              "assert root.MessageId.text() == '${messageId}'\n" +
                              "assert root.CorrelationId.text() == '${correlationId}'\n")
                              "assert root.Text.text() == 'Hello ' + context.getVariable(\"user\")"))
    .header("Operation", "sayHello")
    .header("CorrelationId", "${correlationId}")
    .timeout(5000L);
XML
<receive endpoint="someEndpoint" timeout="5000">
    <message>
        <validate>
            <script type="groovy">
                assert root.children().size() == 4
                assert root.MessageId.text() == '${messageId}'
                assert root.CorrelationId.text() == '${correlationId}'
                assert root.Text.text() == 'Hello ' + context.getVariable("user")
            </script>
        </validate>
    </message>
    <header>
        <element name="Operation" value="sayHello"/>
        <element name="CorrelationId" value="${correlationId}"/>
    </header>
</receive>

The Groovy XmlSlurper validation script goes right into the message-tag instead of an 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.

Java
receive("someEndpoint")
    .validate(groovy().script("assert receivedMessage.getPayload(String.class).contains(\"Hello Citrus!\")\n" +
                              "assert receivedMessage.getHeader(\"Operation\") == 'sayHello'\n" +
                              "context.setVariable(\"request_payload\", receivedMessage.getPayload(String.class))"))
    .timeout(5000L);
XML
<receive endpoint="someEndpoint" 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:

Java
receive("someEndpoint")
    .validate(groovy()
            .script(new ClassPathResource("validationScript.groovy"))
    .timeout(5000L);
XML
<receive endpoint="someEndpoint" timeout="5000">
    <message>
        <validate>
            <script type="groovy" file="classpath:validationScript.groovy"/>
        </validate>
    </message>
</receive>

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

7.4.9. 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). In case the XML document is 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. Citrus can validate XML documents using the schema languages DTD and XSD.

XSD schema repositories

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

Java
@Bean
public XsdSchemaRepository schemaRepository() {
    XsdSchemaRepository repository = new XsdSchemaRepository();
    repository.getSchemas().add(travelAgencySchema());
    repository.getSchemas().add(royalAirlineSchema());
    repository.getSchemas().add(smartAirlineSchema());
    return repository;
}

@Bean
public SimpleXsdSchema travelAgencySchema() {
    return new SimpleXsdSchema(
            new ClassPathResource("classpath:citrus/flightbooking/TravelAgencySchema.xsd"));
}

@Bean
public SimpleXsdSchema royalAirlineSchema() {
    return new SimpleXsdSchema(
            new ClassPathResource("classpath:citrus/flightbooking/RoyalAirlineSchema.xsd"));
}

@Bean
public SimpleXsdSchema smartAirlineSchema() {
    return new SimpleXsdSchema(
            new ClassPathResource("classpath:citrus/flightbooking/SmartAirlineSchema.xsd"));
}
XML
<citrus:schema-repository id="schemaRepository">
    <citrus:schemas>
        <citrus:schema id="travelAgencySchema"
            location="classpath:citrus/flightbooking/TravelAgencySchema.xsd"/>
        <citrus:schema id="royalAirlineSchema"
            location="classpath:citrus/flightbooking/RoyalAirlineSchema.xsd"/>
        <citrus:reference schema="smartAirlineSchema"/>
    </citrus:schemas>
</citrus:schema-repository>

<citrus:schema id="smartAirlineSchema"
      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".
Java
receive("someEndpoint")
    .validate(xpath()
            .expression("//ns1:TestMessage/ns1:MessageHeader/ns1:MessageId", "${messageId}")
            .expression("//ns1:TestMessage/ns1:MessageHeader/ns1:CorrelationId", "${correlationId}")
            .schemaValidation(false)
            .namespaceContext("ns1", "http://citrus.com/namespace"))
    .timeout(5000L);
XML
<receive endpoint="someEndpoint">
    <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>
</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.

WSDL schemas

In SOAP WebServices world the WSDL (WebService Schema Definition Language) defines the structure and 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:

Java
@Bean
public WsdlXsdSchema airlineWsdl() {
    return new WsdlXsdSchema(
            new ClassPathResource("classpath:citrus/flightbooking/AirlineSchema.wsdl"));
}
XML
<citrus:schema id="airlineWsdl"
    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.

Schema collections

Sometimes an 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.

Java
@Bean
public XsdSchemaCollection flightbookingSchemaCollection() {
    XsdSchemaCollection collection = new XsdSchemaCollection();
    collection.getSchemas().add("classpath:citrus/flightbooking/BaseTypes.xsd");
    collection.getSchemas().add("classpath:citrus/flightbooking/AirlineSchema.xsd");
    return collection;
}
XML
<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.

Java
@Bean
public XsdSchemaRepository schemaRepository() {
    XsdSchemaRepository repository = new XsdSchemaRepository();
    repository.getSchemas().add(flightbookingSchemaCollection());
    return repository;
}
XML
<citrus:schema-repository id="schemaRepository">
  <citrus:schemas>
    <citrus:reference schema="flightbookingSchemaCollection"/>
  </citrus:schemas>
</citrus:schema-repository>
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.

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:

Java
@Bean
public XsdSchemaRepository schemaRepository() {
    XsdSchemaRepository repository = new XsdSchemaRepository();
    repository.setSchemaMappingStrategy(schemaMappingStrategy());
    repository.getSchemas().add(helloSchema());
    return repository;
}

@Bean
public TargetNamespaceSchemaMappingStrategy schemaMappingStrategy() {
    return new TargetNamespaceSchemaMappingStrategy();
}

@Bean
public SimpleXsdSchema helloSchema() {
    return new SimpleXsdSchema(
            new ClassPathResource("classpath:citrus/samples/sayHello.xsd"));
}
XML
<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="org.citrusframework.xml.schema.TargetNamespaceSchemaMappingStrategy"/>

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

Schema target namespace
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns="http://citrusframework.org/schemas/sayHello.xsd"
    targetNamespace="http://citrusframework.org/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.xml
<HelloRequest xmlns="http://citrusframework.org/schemas/sayHello.xsd">
   <MessageId>123456789</MessageId>
   <CorrelationId>1000</CorrelationId>
   <User>Christoph</User>
   <Text>Hello Citrus</Text>
</HelloRequest>
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 an XML message. According to this root element you can set up mappings in the schema repository.

Java
@Bean
public XsdSchemaRepository schemaRepository() {
    XsdSchemaRepository repository = new XsdSchemaRepository();
    repository.setSchemaMappingStrategy(schemaMappingStrategy());
    repository.getSchemas().add(helloSchema());
    repository.getSchemas().add(goodbyeSchema());
    return repository;
}

@Bean
public RootQNameSchemaMappingStrategy schemaMappingStrategy() {
    RootQNameSchemaMappingStrategy rootQnameStrategy = new RootQNameSchemaMappingStrategy();
    rootQnameStrategy.getMappings().put("HelloRequest", helloSchema());
    rootQnameStrategy.getMappings().put("GoodbyeRequest", goodbyeSchema());

    return rootQnameStrategy;
}

@Bean
public SimpleXsdSchema helloSchema() {
    return new SimpleXsdSchema(
            new ClassPathResource("classpath:citrus/samples/sayHello.xsd"));
}

@Bean
public SimpleXsdSchema goodbyeSchema() {
    return new SimpleXsdSchema(
            new ClassPathResource("classpath:citrus/samples/sayGoodbye.xsd"));
}
XML
<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="org.citrusframework.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 these dedicated mappings you are able to control which schema is used on an XML request, regardless of target namespace definitions.

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.

Java
@Bean
public XsdSchemaRepository schemaRepository() {
    XsdSchemaRepository repository = new XsdSchemaRepository();
    repository.setSchemaMappingStrategy(schemaMappingStrategy());
    repository.getSchemas().add(helloSchema());
    repository.getSchemas().add(goodbyeSchema());
    return repository;
}

@Bean
public SchemaMappingStrategyChain schemaMappingStrategy() {
    SchemaMappingStrategyChain chain = new SchemaMappingStrategyChain();

    RootQNameSchemaMappingStrategy rootQnameStrategy = new RootQNameSchemaMappingStrategy();
    rootQnameStrategy.getMappings().put("HelloRequest", helloSchema());

    chain.setStrategies(Arrays.asList(
        rootQnameStrategy,
        new TargetNamespaceSchemaMappingStrategy()
    ));

    return chain;
}
XML
<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="org.citrusframework.xml.schema.SchemaMappingStrategyChain">
  <property name="strategies">
    <list>
      <bean class="org.citrusframework.xml.schema.RootQNameSchemaMappingStrategy">
        <property name="mappings">
          <map>
            <entry key="HelloRequest" value="helloSchema"/>
          </map>
        </property>
      </bean>
      <bean class="org.citrusframework.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.

DTD validation

XML DTD (document type definition) is another way to validate the structure of an XML document. Many people say that DTD is outdated and XML schema is the much more efficient way to describe the rules of an 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.

Java
receive("someEndpoint")
    .message()
    .body("<!DOCTYPE root [\n" +
                "<!ELEMENT root (message)>\n" +
                "<!ELEMENT message (text)>\n" +
                "<!ELEMENT text (#PCDATA)>\n" +
            "]>\n" +
            "<root>\n" +
                "<message>\n" +
                    "<text>Hello from Citrus!</text>\n" +
                "</message>\n" +
            "</root>")
    .timeout(5000L);
XML
<receive endpoint="someEndpoint">
    <message schema-validation="false">
        <data>
        <![CDATA[
            <!DOCTYPE root [
                <!ELEMENT root (message)>
                <!ELEMENT message (text)>
                <!ELEMENT text (#PCDATA)>
                ]>
            <root>
                <message>
                    <text>Hello from Citrus!</text>
                </message>
            </root>
        ]]>
        </data>
    </message>
</receive>

The system under test may also send the message with an 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.

Java
receive("someEndpoint")
    .message()
    .body("<!DOCTYPE root SYSTEM \"org/citrusframework/validation/example.dtd\">\n" +
            "<root>\n" +
                "<message>\n" +
                    "<text>Hello from Citrus!</text>\n" +
                "</message>\n" +
            "</root>")
    .timeout(5000L);
XML
<receive endpoint="someEndpoint">
    <message schema-validation="false">
        <data>
        <![CDATA[
            <!DOCTYPE root SYSTEM "org/citrusframework/validation/example.dtd">
            <root>
                <message>
                    <text>Hello from Citrus!</text>
                </message>
            </root>
        ]]>
        <data/>
    </message>
</receive>

7.4.10. XHTML validation

Html message content is hard to verify with XML validation capabilities such as XML tree comparison or XPath support. Usually 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>).

Please add a new library dependency to your 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.

Jtidy library
<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.

Java
@Bean
public XhtmlMessageValidator defaultXhtmlMessageValidator() {
    return new XhtmlMessageValidator();
}
XML
<bean id="defaultXhtmlMessageValidator" class="org.citrusframework.validation.xhtml.XhtmlMessageValidator"/>

Now you can use the XHTML message validation in your test case.

Java
receive("someEndpoint")
    .message()
    .type(MessageType.XHTML)
    .body("<!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>" +
            "</html>")
    .timeout(5000L);
XML
<receive endpoint="someEndpoint">
    <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>
            </html>
          ]]>
        </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. You can also use test variables, ignore element expressions and XPath expressions.

7.5. Schema validation

When structured data is transmitted from one system to another, it is important that both sender and receiver agree on an interface contract. The contract introduces rules of communication for both parties.

Schemas represent the most popular way to define contracts. Citrus is able to manage multiple schemas in the project context. You can define mapping rules to pick the right schema for a message validation.

Let’s start with this chapter by introducing some basic concepts of the schema validation.

7.5.1. Schema definition

Complex applications require multiple schemas that are relevant for different use cases. You should organize these schemas in your test project. First you need to add a schema definition that points to the schema location.

Java
@Bean
public SimpleXsdSchema bookstoreSchema() {
    return new SimpleXsdSchema(new ClassPathResource("classpath:org/citrusframework/xml/BookStore.wsdl"));
}
XML
<citrus:schema id="bookstoreSchema" location="classpath:org/citrusframework/xml/BookStore.wsdl"/>

Please keep in mind, that the id of the schema has to be unique within the context.

The samples above are using XML XSD/WSDL schema definitions. The same approach applies to Json schemas, too. You just need to use the SimpleJsonSchema class in the Java configuration. The XML configuration components derive the schema type automatically based on the file extension (.xsd, .wsdl, .json).

7.5.2. Schema repository

You can now reference the schema definition in a repository component. The repository is able to hold multiple schema definitions.

Java
@Bean
public XsdSchemaRepository schemaRepository() {
    XsdSchemaRepository repository = new XsdSchemaRepository();
    repository.getSchemas().add(bookstoreSchema());
    return repository;
}
XML
<citrus:schema-repository id="schemaRepository">
  <citrus:schemas>
    <citrus:reference schema="bookstoreSchema" />
  </citrus:schemas>
</citrus:schema-repository>

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.

As an alternative to a schema reference you can also provide the schema location in a repository.

Java
@Bean
public XsdSchemaRepository schemaRepository() {
    XsdSchemaRepository repository = new XsdSchemaRepository();
    repository.getLocations().add("classpath:schemas/flightbooking.xsd");
    return repository;
}
XML
<citrus:schema-repository id="schemaRepository">
  <citrus:locations>
    <citrus:location path="classpath:schemas/flightbooking.xsd"/>
  </citrus:locations>
</citrus:schema-repository>

The given location points to the schema definition file. Setting all schemas one by one can be verbose and cumbersome, especially when dealing with lots of schema files. Therefore, Citrus provides schema location patterns which will import all matching schema files within the given location.

Java
@Bean
public XsdSchemaRepository schemaRepository() {
    XsdSchemaRepository repository = new XsdSchemaRepository();
    repository.getLocations().add("classpath:schemas/*.xsd");
    return repository;
}
XML
<citrus:schema-repository id="schemaRepository">
  <citrus:locations>
    <citrus:location path="classpath:schemas/*.xsd"/>
  </citrus:locations>
</citrus:schema-repository>

The schema repository is able to receive many schemas with different locations and schema sources.

Java
@Bean
public SimpleXsdSchema testSchema() {
    return new SimpleXsdSchema(new ClassPathResource("classpath:org/citrusframework/xml/test.xsd"));
}

@Bean
public XsdSchemaRepository schemaRepository() {
    SimpleXsdSchema bookstoreSchema = new SimpleXsdSchema(
            new ClassPathResource("classpath:org/citrusframework/xml/BookStore.wsdl"));

    XsdSchemaRepository repository = new XsdSchemaRepository();
    repository.getSchemas().add(bookstoreSchema);
    repository.getSchemas().add(testSchema());
    repository.getLocations().add("classpath:schemas/*.xsd");
    return repository;
}
XML
<citrus:schema id="testSchema" location="classpath:org/citrusframework/xml/test.xsd"/>

<citrus:schema-repository id="xmlSchemaRepository">
  <citrus:schemas>
    <citrus:schema id="bookstoreSchema" location="classpath:org/citrusframework/xml/BookStore.wsdl"/>
    <citrus:reference schema="testSchema"/>
    <citrus:location path="classpath:schemas/*.xsd"/>
  </citrus:schemas>
</citrus:schema-repository>
The examples in this chapter have been using XML XSD schema repository components. Of course, the same components are available for Json schema repositories, too. By default, the type of the schema repository is type=xml. You can use type=json to mark the schema repository as Json nature. In Java configuration please use the JsonSchemaRepository class.

The schema repository component holds a set of schema files for a project disjoint by their type (xml, json, etc.) and identified by its unique id.

As you can see the schema repository is a simple bean 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.

7.5.3. Schema definition mapping

Depending on the type of message you want to validate, there are different attempts to find the correct schema for the given message. The XML schema repository will apply a mapping strategy that decides which schema should verify the current message. Citrus knows multiple mapping strategies that you can review in chapter XML schema validation.

As a user you always have the chance to explicitly pick the right schema definition for a receive operation. You can overrule all schema mapping strategies in Citrus by directly setting the desired schema in your receiving message action.

Java
receive(httpMessageEndpoint)
    .message()
    .validate(
        xml().schema("helloSchema")
    );
XML
<receive endpoint="httpMessageEndpoint">
    <message schema="helloSchema">
      <payload>
        ...
      </payload>
    </message>
</receive>

In the example above the tester explicitly sets a schema definition in the receive action (schema="helloSchema"). The schema value refers to named schema bean defined in the project context (e.g. Spring 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.

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.

Java
receive(httpMessageEndpoint)
    .message()
    .validate(
        xml().schemaRepository("helloSchemaRepository")
    );
XML
<receive endpoint="httpMessageEndpoint">
    <message schema-repository="helloSchemaRepository">
      <payload>
        ...
      </payload>
    </message>
</receive>

The schema-repository attribute refers to a Citrus schema repository component which is defined as bean in the project context.

7.6. Plain text validation

Plain text message validation performs an exact Java String match of received and expected message payloads.

Plaintext message validation is not enabled by default in your project. You need to add the validation module to your project as a Maven dependency.

Plaintext validation module dependency
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-validation-text</artifactId>
  <version>${citrus.version}</version>
</dependency>

This adds the default message validator for plaintext messages. 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.

Java
@Bean
public PlainTextMessageValidator defaultPlaintextMessageValidator() {
    return new PlainTextMessageValidator();
}
XML
<bean id="defaultPlaintextMessageValidator" class="org.citrusframework.validation.text.PlainTextMessageValidator"/>

Citrus will try to auto-guess the appropiate message validator for the incoming message. You can explicitly set the message type on the receive action so Citrus knows how to apply plaintext message validation.

Java
receive("someEndpoint")
    .message()
    .type(MessageType.PLAINTEXT)
    .body("Hello World!");
XML
<receive endpoint="someEndpoint">
    <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.

Of course test variables are supported in the plaintext payloads. The variables are replaced before processing the message template.

Java
receive("someEndpoint")
    .message()
    .type(MessageType.PLAINTEXT)
    .body("${hello} ${world}!");
XML
<receive endpoint="someEndpoint">
    <message type="plaintext">
        <data>${hello} ${world}!</data>
    </message>
</receive>

7.6.1. Whitespace characters

Plaintext message payloads may only differ in system-dependent line separator characters (CR, LF, CRLF). By default, the plaintext 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:

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

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

Plaintext validation settings
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 plaintext message validator bean:

Java
@Bean
public PlainTextMessageValidator defaultPlaintextMessageValidator() {
    PlainTextMessageValidator validatoe = new PlainTextMessageValidator();
    validator.setIgnoreNewLineType(true);
    validator.setIgnoreWhitespace(true);
    return validator;
}
XML
<bean id="defaultPlaintextMessageValidator" class="org.citrusframework.validation.text.PlainTextMessageValidator">
  <property name="ignoreNewLineType" value="true"/>
  <property name="ignoreWhitespace" value="true"/>
</bean>

7.6.2. Ignoring text parts

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

Received text
Your current id is "1234567890"

In the plaintext validation you need to ignore the actual id value due to some reason. Maybe the id is generated on a foreign application and you 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 plaintext 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. In case 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:

Received 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.

7.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:

Received 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 ${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:

Received 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. In addition, the European date representation works fine here, too because dots and dashes are automatically included:

Received text
Today is "24.12.2017"

7.6.4. Gzip validation

Gzip is a message compression library to optimize the message transport of large content. Citrus is able to handle compressed message payloads on send and receive operations. Sending compressed data sets the message type to gzip.

Java
send("someEndpoint")
    .message()
    .type(MessageType.GZIP)
    .body("Hello World!")
XML
<send endpoint="someEndpoint">
    <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.

Java
@Bean
public GzipBinaryBase64MessageValidator defaultGzipBinaryBase64MessageValidator() {
    return new GzipBinaryBase64MessageValidator();
}
XML
<bean id="defaultGzipBinaryBase64MessageValidator"
      class="org.citrusframework.validation.text.GzipBinaryBase64MessageValidator"/>

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

Java
receive("someEndpoint")
    .message()
    .type(MessageType.GZIP_BASE64)
    .body("citrus:encodeBase64('Hello World!')")
XML
<receive endpoint="someEndpoint">
    <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.

7.7. Binary validation

Binary message validation is not an easy task in particular when it comes to compare data with a given control message.

Binary message validation is not enabled by default in your project. You need to add the validation module to your project as a Maven dependency.

Binary validation module dependency
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-validation-binary</artifactId>
  <version>${citrus.version}</version>
</dependency>

There are basically two ways in Citrus how to compare binary message content for validation purpose.

7.7.1. Stream message validation

A first approach to validate incoming binary message content is to compare the binary stream data with an expected stream. This comparison is straight forward as each byte in the binary stream is compared to an expected stream.

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.

Java
@Bean
public BinaryMessageValidator defaultBinaryMessageValidator() {
    return new BinaryMessageValidator();
}
XML
<bean id="defaultBinaryMessageValidator" class="org.citrusframework.validation.text.BinaryMessageValidator"/>

You can use the binary message type in a receive action in order to enable this stream comparison during validation.

Java
.receive("someEndpoint")
    .message(new DefaultMessage(FileCopyUtils.copyToByteArray(new ClassPathResource("templates/foo.png").getFile())))
    .type(MessageType.BINARY);
}
XML
<receive endpoint="someEndpoint">
    <message type="binary">
        <resource file="classpath:templates/foo.png"/>
    </message>
</receive>

It is very important to set the message type to MessageType.BINARY as this is the message type that is automatically handled by the binary stream message validator.

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.

Java
send("someEndpoint")
    .message()
    .type(MessageType.BINARY)
    .body("Hello World")
XML
<send endpoint="someEndpoint">
    <message type="binary">
        <data>Hello World!</data>
    </message>
</send>

7.7.2. Base64 message validation

Another way to validate binary message content is to use base64 String encoding. The binary data is encoded as base64 character sequence and therefore 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.

Java
@Bean
public BinaryBase64MessageValidator defaultBinaryBase64MessageValidator() {
    return new BinaryBase64MessageValidator();
}
XML
<bean id="defaultBinaryBase64MessageValidator" class="org.citrusframework.validation.text.BinaryBase64MessageValidator"/>

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

Java
receive("someEndpoint")
    .message()
    .body("citrus:encodeBase64('Hello World!')")
XML
<receive endpoint="someEndpoint">
    <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.

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.

Java
send("someEndpoint")
    .message()
    .body("citrus:encodeBase64('Hello World!')")
XML
<send endpoint="someEndpoint">
    <message>
        <data>citrus:encodeBase64('Hello World!')</data>
    </message>
</send>

7.8. Hamcrest validation

Hamcrest validation empowers the validation capabilities by adding special assertions.

Hamcrest message validation capability is not enabled by default in your project. You need to add the validation module to your project as a Maven dependency.

Binary validation module dependency
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-validation-hamcrest</artifactId>
  <version>${citrus.version}</version>
</dependency>

You can use the Hamcrest assertions when receiving a message in order to validate elements, for instance with XPath.

Java
receive("someEndpoint")
    .validate(xpath()
        .expression("/TestRequest/Error", anyOf(empty(), nullValue()))
        .expression("number:/TestRequest/Status[.='success']", greaterThan(0.0))
        .expression("integer:/TestRequest/Status[.='failed']", lowerThan(1))
        .expression("node-set:/TestRequest/OrderType", hasSize(3));
If you want to match text containing any of the following characters: ' , ( ) You need to enclose the respective string in quotation marks when defining your matcher. If you intend to match an actual single quote, it should be escaped with a backslash (\').

For example:

anyOf(equalTo('text containing a \\' (quote) and a , (comma)  '), anyOf(isEmptyOrNullString()))
anyOf(equalTo('text containing a backslash and quote \\\\' and a , (comma)  '), anyOf(isEmptyOrNullString()))
XML
<receive endpoint="someEndpoint">
  <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>
</receive>

7.9. Custom validation

In a previous section you have seen how to customize the global set of available message validators in a Citrus project using the validator registry.

7.9.1. Custom message validator

You can also explicitly use a custom message validator in a receive test action. This approach skips the automatic message validator resolving mechanism, and the receive action uses the defined validator implementation.

Java
receive("someEndpoint")
    .validator(groovyJsonMessageValidator)
    .message()
    .type(MessageType.JSON)
    .validate(groovy()
                .script("assert json.type == 'read'\n" +
                  "assert json.mbean == 'java.lang:type=Memory'\n" +
                  "assert json.attribute == 'HeapMemoryUsage'\n" +
                  "assert json.value == '${heapUsage}'");
XML
<receive endpoint="someEndpoint">
    <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>

The receive action defines the message validator to use in this specific use case. You can set a custom message validator implementation here.

Be careful when overwriting default message validation behavior. Setting a specific message validator has the effect that your receive action may not use the other default validators. The explicit validator definition in a receive action is exclusive so no further message validator resolving is performed on this receive action.

You may want to set multiple validators here in order to meet your validation requirements. For instance when setting a DomXmlMessageValidator explicitly you may not be able to use the XpathMessageValidator capabilities on that specific receive action anymore. Fortunately you can set multiple validators on a receive action that will all perform validation tasks on the received message.

Java
receive("someEndpoint")
    .validators(myXmlMessageValidator, defaultXpathMessageValidator)
    .message()
    .body("...")
    .validate(xpath().expression("//some/xpath/expression", "someControlValue"));
XML
<receive endpoint="someEndpoint">
    <message type="json" validators="myXmlMessageValidator,defaultXpathMessageValidator">
        <payload>...</data>
        <validate path="//some/xpath/expression" value="someControlValue"/>
    </message>
</receive>

7.9.2. Validation processor

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 processor functionality. With this feature you can marshal/unmarshal message payloads and code validation steps on Java objects.

Validation processor usage
receive(bookResponseEndpoint)
    .validate(new XmlMarshallingValidationProcessor<AddBookResponseMessage>() {
        @Override
        public void validate(AddBookResponseMessage response, MessageHeaders headers) {
            Assert.isTrue(response.isSuccess());
        }
    });

By default, the validation processor 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.

Use autowired unmarshaller
@Autowired
private Unmarshaller unmarshaller;

@CitrusTest
public void receiveMessageTest() {
    receive(bookResponseEndpoint)
        .validate(new XmlMarshallingValidationProcessor<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.

Message body marshalling
@Autowired
private Marshaller marshaller;

@CitrusTest
public void sendMessageTest() {
    send(bookRequestEndpoint)
        .message()
        .body(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 an 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.

8. 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.

8.1. Send

Integration test scenarios need to trigger business logic on foreign applications by calling service interfaces on the system under test. The Citrus test is able to send messages over various message transports (e.g. Http REST, JMS, Kafka, filte transfer).

figure_001.jpg

A message consists of a message header (name-value pairs) and a message body. Later in this section we will see different ways of constructing a message with body and header values.

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

    $(send("helloService")
        .message()
        .name("helloMessage")
        .header("Operation", "sayHello")
        .header("RequestTag", "${messageId}")
        .body("""
            <HelloMessage>
                <Text>Hello Citrus!</Text>
            </HelloMessage>
        """)
    );
}
XML
<test name="ReceiveMessageTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <variables>
        <variable name="text" value="Hello Citrus!"/>
        <variable name="messageId" value="Mx1x123456789"/>
    </variables>

    <actions>
        <send endpoint="helloService">
            <message name="helloMessage">
                <headers>
                    <header name="Operation" value="sayHello"/>
                    <header name="MessageId" value="${messageId}"/>
                </headers>
                <body>
                    <![CDATA[
                    <HelloMessage>
                        <Text>Hello Citrus!</Text>
                    </HelloMessage>
                    ]]>
                </body>
            </message>
        </send>
    </actions>
</test>
YAML
name: ReceiveMessageTest
variables:
    - name: "text"
      value: "Hello Citrus!"
    - name: "messageId"
      value: "Mx1x123456789"
actions:
  - send:
      endpoint: "helloService"
      message:
        name: "helloMessage"
        headers:
          - name: "Operation"
            value: "sayHello"
          - name: "MessageId"
            value: '${messageId}'
        body: |
          <HelloMessage>
              <Text>Hello Citrus!</Text>
          </HelloMessage>
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="SendMessageTest">
        <variables>
            <variable name="text" value="Hello Citrus!"/>
            <variable name="messageId" value="Mx1x123456789"/>
        </variables>

        <actions>
            <send endpoint="helloService">
                <message name="helloMessage">
                    <payload>
                        <![CDATA[
                        <HelloMessage>
                            <Text>${text}</Text>
                        </HelloMessage>
                        ]]>
                    </payload>
                </message>
                <header>
                    <element name="Operation" value="sayHello"/>
                    <element name="MessageId" value="${messageId}"/>
                </header>
            </send>
        </actions>
    </testcase>
</spring:beans>

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 let’s 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 "helloService" 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.

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:

8.1.1. Send message body

The most important thing when dealing with sending actions is to prepare the message payload and header. You can specify the body as nested String value.

Java
@CitrusTest
public void sendMessageTest() {
    $(send("helloService")
        .message()
        .body("""
            <HelloMessage>
                <Text>Hello Citrus!</Text>
            </HelloMessage>
        """)
    );
}
XML
<test name="SendMessageTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <send endpoint="helloService">
            <message>
                <body>
                    <![CDATA[
                    <HelloMessage>
                        <Text>Hello Citrus!</Text>
                    </HelloMessage>
                    ]]>
                </body>
            </message>
        </receive>
    </actions>
</test>
YAML
name: SendMessageTest
actions:
  - send:
      endpoint: "helloService"
      message:
        body: |
          <HelloMessage>
              <Text>Hello Citrus!</Text>
          </HelloMessage>
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="SendMessageTest">
        <actions>
            <send endpoint="helloService">
                <message>
                    <payload>
                        <![CDATA[
                        <HelloMessage>
                            <Text>Hello Citrus!</Text>
                        </HelloMessage>
                        ]]>
                    </payload>
                </message>
            </receive>
        </actions>
    </testcase>
</spring:beans>

A simple way of defining the message body content is to provide the message body as a String. You can do this in the different supported languages by embedding the message content in the body section of the send action.

In XML you can embed the content as a CDATA section and in Java, or YAML you may want to use text blocks.

When the message body content is an XML payload you can also use nested XML elements in the XML domain specific languages as the next example shows:

XML
<test name="ReceiveMessageTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <send endpoint="helloService">
            <message name="helloRequest">
                <body>
                    <HelloMessage xmlns="http://sample.org/">
                        <Text>Hello Citrus!</Text>
                    </HelloMessage>
                </body>
            </message>
        </send>
    </actions>
</test>
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="ReceiveMessageTest">
        <actions>
            <send endpoint="helloService">
              <message>
                <payload>
                  <HelloMessage xmlns="http://sample.org/">
                      <Text>Hello Citrus!</Text>
                  </HelloMessage>
                </payload>
              </message>
            </send>
        </actions>
    </testcase>
</spring:beans>
In XML you can use nested XML elements or CDATA sections. Sometimes the nested XML message payload elements may cause XSD schema 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 lose the XSD auto-completion features many XML editors offer when constructing your payload.

Message body content may be quite huge, so you can also load the message content form an external file resource. The file path is given as either a classpath or file system resource.

When writing tests in Java you can use one of the classpath or file system resource implementations to resolve the file path. In XML and other languages you may use a resource path given as: file="classpath:path/to/request.xml". The file path prefix indicates the file resource type (file: or classpath:), so the file location is resolved either as file system resource (file:) or classpath resource (classpath:).

Java
@CitrusTest
public void sendMessageTest() {
    $(send("helloService")
        .message()
        .body(new ClassPathResource("path/to/request.xml"))
    );
}
XML
<test name="SendMessageTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <send endpoint="helloService">
            <message>
                <body>
                    <resource file="classpath:path/to/request.xml"/>
                </body>
            </message>
        </send>
    </actions>
</test>
YAML
name: SendMessageTest
actions:
  - send:
      endpoint: "helloService"
      message:
        body:
          resource:
            file: "classpath:path/to/request.xml"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="SendMessageTest">
        <actions>
            <send endpoint="helloService">
                <message>
                    <resource file="classpath:path/to/request.xml" />
                </message>
            </send>
        </actions>
    </testcase>
</spring:beans>

In addition to defining message payloads as normal Strings and via external file resource (classpath and file system) you can also provide a POJO model object as a message payload. The model object will be serialized with a marshaller or object mapper implementation which gets loaded from the Citrus context.

You can use the marshalling message payload builders in a send action as follows.

Marshalling message payload builder
$(send("helloService")
    .message()
    .body(marshal(new TestRequest("Hello Citrus!")))
);

The send action uses the marshalling message builder provided with Citrus and just provides the model object new TestRequest(). The marshalling message builder automatically loads a proper XML message marshaller that should be available as a bean in the project context (e.g. the Spring application context). By default, Citrus is searching for a bean of type org.citrusframework.xml.Marshaller. You can add the marshaller to your project context as a bean.

Marshaller bean
@Bean
public Marshaller xmlMarshaller() {
    return new Jaxb2Marshaller();
}

Now Citrus is able to automatically resolve the marshaller from the project context as soon as the receive action uses the model object in combination with the marshal instruction.

When you have multiple message marshaller instances in your project context you have to tell Citrus which one to use in this particular receive message action.

Reference message marshaller
$(send("helloService")
    .message()
    .body(marshal(new TestRequest("Hello Citrus!"), "myMessageMarshallerBean"))
);

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

You can implement your own message payload builder or use one of the provided Citrus message payload builders.

Custom message payload builder
@Autowired
private MessagePayloadBuilder mySpecialPayloadBuilder = new FooPayloadBuilder();

$(send("helloService")
    .message()
    .body(mySpecialPayloadBuilder)
);

The message payload builder must implement the MessagePayloadBuilder interface with the method buildPayload(TestContext context).

MessagePayloadBuilder interface
public class FooPayloadBuilder implements MessagePayloadBuilder {

    @Override
    public Object buildPayload(TestContext context) {
        // return some special payload
        return new FooModel();
    }
}

You can also use a Citrus message instance directly. Citrus provides different message implementations with fluent APIs to have a convenient way of setting properties (e.g. HttpMessage, MailMessage, FtpMessage, SoapMessage, …​).

Citrus message object
$(send("helloService")
    .message(new DefaultMessage("Hello World!"))
);

You can explicitly overwrite some message values in the body before the validations is performed. This is for overwriting specific message elements with variable values for instance. Also, you can overwrite values using XPath (xpath) or JsonPath (json-path) expressions.

Java
@CitrusTest
public void jsonPathTest() {
    $(receive("someEndpoint")
        .message()
        .type(MessageType.JSON)
        .body(new ClassPathResource("path/to/request.xml"))
        .process(jsonPath()
            .expression("$.user.name", "Penny")
            .expression("$['user']['name']", "${userName}"));
    );
}
XML
<test name="JsonPathTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <receive endpoint="someEndpoint">
            <message type="json">
                <body>
                    <resource file="classpath:path/to/request.xml" />
                </body>
                <expression path="$.user.name" value="Penny"/>
                <expression path="$['user']['name']" value="${userName}"/>
            </message>
        </receive>
    </actions>
</test>
YAML
name: JsonPathTest
actions:
  - receive:
      endpoint: "someEndpoint"
      message:
        type: json
        resource:
          file: "classpath:path/to/request.xml"
        expression:
          - path: '$.user.name'
            value: "Penny"
          - path: '$["user"]["name"]'
            value: '${userName}'
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="JsonPathTest">
        <actions>
            <receive endpoint="someEndpoint">
                <message type="json">
                    <resource file="classpath:path/to/request.xml" />
                    <element path="$.user.name" value="Penny"/>
                    <element path="$['user']['name']" value="${userName}"/>
                </message>
            </receive>
        </actions>
    </testcase>
</spring:beans>

8.1.2. Send message headers

Defining the message header is an essential part. Citrus uses name-value pairs like "Operation" and "MessageId" in the next example to set message header entries.

Depending on what message endpoint and which message transport underneath is used the header values will be shipped in different ways. In JMS for instance 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 knows how to set headers on different message transports and aims to do the hard work for you .

Java
@CitrusTest
public void messageHeaderTest() {
    $(send("helloService")
        .message()
        .header("Operation", "sayHello")
        .header("MessageId", "${messageId}")
        .body("""
            <TestMessage xmlns="http://citrusframework.org/schema">
                <Text>Hello!</Text>
            </TestMessage>
        """)
    );
}
XML
<test name="MessageHeaderTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <send endpoint="someEndpoint">
            <message>
                <headers>
                    <header name="Operation" value="sayHello"/>
                </headers>
                <body>
                    <payload>
                        <TestMessage xmlns="http://citrusframework.org/schema">
                            <Text>Hello!</Text>
                        </TestMessage>
                    </payload>
                </body>
            </message>
        </send>
    </actions>
</test>
YAML
name: MessageHeaderTest
actions:
  - send:
      endpoint: "helloService"
      message:
        headers:
          - name: "Operation"
            value: "sayHello"
        body:
          data: |
            <TestMessage xmlns="http://citrusframework.org/schema">
                <Text>Hello!</Text>
            </TestMessage>
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="MessageHeaderTest">
        <actions>
            <send endpoint="helloService">
                <message>
                    <payload>
                        <TestMessage xmlns="http://citrusframework.org/schema">
                            <Text>Hello!</Text>
                        </TestMessage>
                    </payload>
                </message>
                <header>
                    <element name="Operation" value="sayHello"/>
                </header>
            </send>
        </actions>
    </testcase>
</spring:beans>

The message headers to send are defined by a simple name and value pairs. Of course you can use test variables in header values as well.

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.

8.1.3. Groovy Markup builder

With the Groovy markup builder you can build XML message body content in a simple way, without having to write the typical XML overhead.

The Groovy test action support lives in a separate module. You need to add the module to your project to use the functionality.
citrus-groovy dependency module
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-groovy</artifactId>
  <version>${citrus.version}</version>
</dependency>

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 body XML data we write a Groovy script snippet.

Java
@CitrusTest
public void scriptMessageBuilderTest() {
    $(send("helloService")
        .message()
        .body(new GroovyScriptPayloadBuilder("""
                    markupBuilder.TestRequest(xmlns: 'https://citrus.schemas/samples/sayHello.xsd') {
                        Message('Hello World!')
                    }
        """))
    );
}
XML
<test name="ScriptMessageBuilderTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <send endpoint="helloService">
            <message>
                <body>
                    <builder type="groovy">
                        markupBuilder.TestRequest(xmlns: 'https://citrus.schemas/samples/sayHello.xsd') {
                            Message('Hello World!')
                        }
                    </builder>
                </body>
            </message>
        </send>
    </actions>
</test>
YAML
name: ScriptMessageBuilderTest
actions:
  - send:
      endpoint: "helloService"
      message:
        builder:
          type: "groovy"
          value: |
            markupBuilder.TestRequest(xmlns: 'https://citrus.schemas/samples/sayHello.xsd') {
                Message('Hello World!')
            }
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="ScriptMessageBuilderTest">
        <actions>
            <send endpoint="helloService">
              <message>
                <builder type="groovy">
                    markupBuilder.TestRequest(xmlns: 'https://citrus.schemas/samples/sayHello.xsd') {
                        Message('Hello World!')
                    }
                </builder>
              </message>
            </send>
        </actions>
    </testcase>
</spring:beans>

The Groovy markup builder generates the XML message body with following content:

Genereted markup
<TestRequest xmlns="https://citrus.schemas/samples/sayHello.xsd">
  <Message>Hello World</Message>
</TestRequest>

We use the builder element with type groovy and the markup builder 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 markup builder 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 markup builder extension:

  • The markup builder 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 markup builder script may also be used as external file resource:

Java
@CitrusTest
public void scriptMessageBuilderTest() {
    $(send("helloService")
        .message()
        .body(new GroovyFileResourcePayloadBuilder("classpath:path/to/helloRequest.groovy"))
    );
}
XML
<test name="ScriptMessageBuilderTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <send endpoint="helloService">
            <message>
                <body>
                    <builder type="groovy" file="classpath:path/to/helloRequest.groovy"/>
                </body>
            </message>
        </send>
    </actions>
</test>
YAML
name: ScriptMessageBuilderTest
actions:
  - send:
      endpoint: "helloService"
      message:
        builder:
          type: "groovy"
          file: "classpath:path/to/helloRequest.groovy"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="ScriptMessageBuilderTest">
        <actions>
            <send endpoint="helloService">
              <message>
                <builder type="groovy" file="classpath:path/to/helloRequest.groovy"/>
              </message>
            </send>
        </actions>
    </testcase>
</spring:beans>

The markup builder implementation in Groovy offers great possibilities in defining message body content. We do not need to write XML the tag overhead anymore. The approach also enables us to construct complex message body content with Groovy script logic like iterations and conditional elements. For detailed markup builder descriptions please see the official Groovy documentation.

8.2. Receive

Receiving and validating messages is an essential part of an integration test. You can perform assertions and checks on incoming messages in order to verify that everything works as expected.

A message consists of a message header (name-value pairs) and a message body. You can specify expected message content on a receive message test action. Citrus will perform validation steps and raise errors in case the incoming message does not match these expectations.

Java
@CitrusTest
public void receiveMessageTest() {
    $(receive("helloService")
        .message()
        .name("helloRequest")
        .header("Operation", "sayHello")
        .header("MessageId", "${messageId}")
        .body("""
            <HelloMessage>
                <Text>Hello Citrus!</Text>
            </HelloMessage>
        """)
    );
}
XML
<test name="ReceiveMessageTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <receive endpoint="helloService">
            <message name="helloRequest">
                <headers>
                    <header name="Operation" value="sayHello"/>
                    <header name="MessageId" value="${messageId}"/>
                </headers>
                <body>
                    <![CDATA[
                    <HelloMessage>
                        <Text>Hello Citrus!</Text>
                    </HelloMessage>
                    ]]>
                </body>
            </message>
        </receive>
    </actions>
</test>
YAML
name: ReceiveMessageTest
actions:
  - receive:
      endpoint: "helloService"
      message:
        name: "helloRequest"
        headers:
          - name: "Operation"
            value: "sayHello"
          - name: "MessageId"
            value: '${messageId}'
        body: |
          <HelloMessage>
              <Text>Hello Citrus!</Text>
          </HelloMessage>
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="ReceiveMessageTest">
        <actions>
            <receive endpoint="helloService">
                <message name="helloRequest">
                    <payload>
                        <![CDATA[
                        <HelloMessage>
                            <Text>Hello Citrus!</Text>
                        </HelloMessage>
                        ]]>
                    </payload>
                </message>
                <header>
                    <element name="Operation" value="sayHello"/>
                    <element name="MessageId" value="${messageId}"/>
                </header>
            </receive>
        </actions>
    </testcase>
</spring:beans>

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 test action waits for a message to arrive. The whole test execution is blocked while waiting for the expected message. This is important to ensure the step by step test workflow processing. Of course, you can specify a message timeout setting 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 case the message arrives in time, the test action moves on to validate the message content as a next step. The user is able to choose from different validation capabilities. On the one hand you can specify a whole message body content 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 validated.

In addition to verifying the message body content, Citrus will also perform validation on the received message header values. Test variable usage is supported as usual during the whole validation process for body 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.

8.2.1. Validate message body

The most detailed validation of incoming messages is to define some expected message body. The Citrus message validator will then perform a detailed message body comparison. The incoming message has to match exactly to the expected message body. 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 body 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 body content in receive action:

Java
@CitrusTest
public void receiveMessageTest() {
    $(receive("helloService")
        .message()
        .body("""
            <HelloMessage>
                <Text>Hello Citrus!</Text>
            </HelloMessage>
        """)
    );
}
XML
<test name="ReceiveMessageTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <receive endpoint="helloService">
            <message>
                <body>
                    <![CDATA[
                    <HelloMessage>
                        <Text>Hello Citrus!</Text>
                    </HelloMessage>
                    ]]>
                </body>
            </message>
        </receive>
    </actions>
</test>
YAML
name: ReceiveMessageTest
actions:
  - receive:
      endpoint: "helloService"
      message:
        body: |
          <HelloMessage>
              <Text>Hello Citrus!</Text>
          </HelloMessage>
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="ReceiveMessageTest">
        <actions>
            <receive endpoint="helloService">
                <message>
                    <payload>
                        <![CDATA[
                        <HelloMessage>
                            <Text>Hello Citrus!</Text>
                        </HelloMessage>
                        ]]>
                    </payload>
                </message>
            </receive>
        </actions>
    </testcase>
</spring:beans>

A simple way of defining the expected message body content is to provide the message body as a String. You can do this in the different supported languages by embedding the message content in the body section of the receive action.

In XML you can embed the content as a CDATA section and in Java, or YAML you may want to use text blocks.

When the message body content is an XML payload you can also use nested XML elements in the XML domain specific languages as the next example shows:

XML
<test name="ReceiveMessageTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <receive endpoint="helloService">
            <message name="helloRequest">
                <body>
                    <HelloMessage xmlns="http://sample.org/">
                        <Text>Hello Citrus!</Text>
                    </HelloMessage>
                </body>
            </message>
        </receive>
    </actions>
</test>
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="ReceiveMessageTest">
        <actions>
            <receive endpoint="helloService">
              <message>
                <payload>
                  <HelloMessage xmlns="http://sample.org/">
                      <Text>Hello Citrus!</Text>
                  </HelloMessage>
                </payload>
              </message>
            </receive>
        </actions>
    </testcase>
</spring:beans>
In XML you can use nested XML elements or CDATA sections. Sometimes the nested XML message payload elements may cause XSD schema 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 lose the XSD auto-completion features many XML editors offer when constructing your payload.

Message body content may be quite huge, so you can also load the message content form an external file resource. The file path is given as either a classpath or file system resource.

When writing tests in Java you can use one of the classpath or file system resource implementations to resolve the file path. In XML and other languages you may use a resource path given as: file="classpath:path/to/request.xml". The file path prefix indicates the file resource type (file: or classpath:), so the file location is resolved either as file system resource (file:) or classpath resource (classpath:).

Java
@CitrusTest
public void receiveMessageTest() {
    $(receive("helloService")
        .message()
        .body(new ClassPathResource("path/to/request.xml"))
    );
}
XML
<test name="ReceiveMessageTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <receive endpoint="helloService">
            <message>
                <body>
                    <resource file="classpath:path/to/request.xml"/>
                </body>
            </message>
        </receive>
    </actions>
</test>
YAML
name: ReceiveMessageTest
actions:
  - receive:
      endpoint: "helloService"
      message:
        body:
          resource:
            file: "classpath:path/to/request.xml"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="ReceiveMessageTest">
        <actions>
            <receive endpoint="helloService">
                <message>
                    <resource file="classpath:path/to/request.xml" />
                </message>
            </receive>
        </actions>
    </testcase>
</spring:beans>

In addition to defining message payloads as normal Strings and via external file resource (classpath and file system) you can also provide a POJO model object as a message payload. The model object will be serialized with a marshaller or object mapper implementation which gets loaded from the Citrus context.

You can use the marshalling message payload builders in a receive action as follows.

Marshalling message payload builder
$(receive("helloService")
    .message()
    .body(marshal(new TestRequest("Hello Citrus!")))
);

The receive action uses the marshalling message builder provided with Citrus and just provides the model object new TestRequest(). The marshalling message builder automatically loads a proper XML message marshaller that should be available as a bean in the project context (e.g. the Spring application context). By default, Citrus is searching for a bean of type org.citrusframework.xml.Marshaller. You can add the marshaller to your project context as a bean.

Marshaller bean
@Bean
public Marshaller xmlMarshaller() {
    return new Jaxb2Marshaller();
}

Now Citrus is able to automatically resolve the marshaller from the project context as soon as the receive action uses the model object in combination with the marshal instruction.

When you have multiple message marshaller instances in your project context you have to tell Citrus which one to use in this particular receive message action.

Reference message marshaller
$(receive("helloService")
    .message()
    .body(marshal(new TestRequest("Hello Citrus!"), "myMessageMarshallerBean"))
);

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

You can implement your own message payload builder or use one of the provided Citrus message payload builders.

Custom message payload builder
@Autowired
private MessagePayloadBuilder mySpecialPayloadBuilder = new FooPayloadBuilder();

$(receive("helloService")
    .message()
    .body(mySpecialPayloadBuilder)
);

The message payload builder must implement the MessagePayloadBuilder interface with the method buildPayload(TestContext context).

MessagePayloadBuilder interface
public class FooPayloadBuilder implements MessagePayloadBuilder {

    @Override
    public Object buildPayload(TestContext context) {
        // return some special payload
        return new FooModel();
    }
}

You can also use a Citrus message instance directly. Citrus provides different message implementations with fluent APIs to have a convenient way of setting properties (e.g. HttpMessage, MailMessage, FtpMessage, SoapMessage, …​).

Citrus message object
$(receive("helloService")
    .message(new DefaultMessage("Hello World!"))
);

You can explicitly overwrite some message values in the body before the validations is performed. This is for overwriting specific message elements with variable values for instance. Also, you can overwrite values using XPath (xpath) or JsonPath (json-path) expressions.

Java
@CitrusTest
public void jsonPathTest() {
    $(receive("someEndpoint")
        .message()
        .type(MessageType.JSON)
        .body(new ClassPathResource("path/to/request.xml"))
        .validate(jsonPath()
            .expression("$.user.name", "Penny")
            .expression("$['user']['name']", "${userName}"))
    );
}
XML
<test name="JsonPathTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <receive endpoint="someEndpoint">
            <message type="json">
                <body>
                    <resource file="classpath:path/to/request.xml" />
                </body>
            </message>
            <validate path="$.user.name" value="Penny"/>
            <validate path="$['user']['name']" value="${userName}"/>
        </receive>
    </actions>
</test>
YAML
name: JsonPathTest
actions:
  - receive:
      endpoint: "someEndpoint"
      message:
        type: json
        resource:
          file: "classpath:path/to/request.xml"
        validate:
          jsonPath:
            - expression: '$.user.name'
              value: "Penny"
            - expression: '$["user"]["name"]'
              value: '${userName}'
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="JsonPathTest">
        <actions>
            <receive endpoint="someEndpoint">
                <message type="json">
                    <resource file="classpath:path/to/request.xml" />
                    <validate path="$.user.name" value="Penny"/>
                    <validate path="$['user']['name']" value="${userName}"/>
                </message>
            </receive>
        </actions>
    </testcase>
</spring:beans>

In addition to that you can ignore some elements that are skipped in comparison. We will describe this later on in this section. Now let’s continue with message header validation.

8.2.2. Validate message headers

Message headers are used widely in enterprise messaging. The message headers often represent a critical part of the message semantics and need to be validated, too. Citrus is able to validate message headers by name and value.

Java
@CitrusTest
public void messageHeaderTest() {
    $(receive("helloService")
        .message()
        .header("Operation", "sayHello")
        .body("""
            <TestMessage xmlns="http://citrusframework.org/schema">
                <Text>Hello!</Text>
            </TestMessage>
        """)
    );
}
XML
<test name="MessageHeaderTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <receive endpoint="someEndpoint">
            <message>
                <headers>
                    <header name="Operation" value="sayHello"/>
                </headers>
                <body>
                    <payload>
                        <TestMessage xmlns="http://citrusframework.org/schema">
                            <Text>Hello!</Text>
                        </TestMessage>
                    </payload>
                </body>
            </message>
        </receive>
    </actions>
</test>
YAML
name: MessageHeaderTest
actions:
  - receive:
      endpoint: "helloService"
      message:
        headers:
          - name: "Operation"
            value: "sayHello"
        body:
          data: |
            <TestMessage xmlns="http://citrusframework.org/schema">
                <Text>Hello!</Text>
            </TestMessage>
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="MessageHeaderTest">
        <actions>
            <receive endpoint="helloService">
                <message>
                    <payload>
                        <TestMessage xmlns="http://citrusframework.org/schema">
                            <Text>Hello!</Text>
                        </TestMessage>
                    </payload>
                </message>
                <header>
                    <element name="Operation" value="sayHello"/>
                </header>
            </receive>
        </actions>
    </testcase>
</spring:beans>

The expected message headers are defined by a name and value pair. Citrus will check that the expected message header is present and will verify the value accordingly. 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.

Header definition in Java DSL is straight forward as we just define name and value as usual. This completes tha basic message validation in Citrus. The individual 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.

8.2.3. Ignore elements

Sometimes a tester can not verify all values because specifying expected values is not possible for non deterministic values (e.g. timestamps, dynamic and generated identifiers).

You can use a path expressions (e.g. Xpath, JsonPath) to ignoring a very specific entry in the message body.

Java
@CitrusTest
public void ignorePathTest() {
    $(receive(someEndpoint)
        .message()
        .type(MessageType.JSON)
        .body("""
            {
              "users":
              [{
                "name": "Jane",
                "token": "?",
                "lastLogin": 0
              },
              {
                "name": "Penny",
                "token": "?",
                "lastLogin": 0
              },
              {
                "name": "Mary",
                "token": "?",
                "lastLogin": 0
              }]
            }
        """)
        .validate(json()
                    .ignore("$.users[*].token")
                    .ignore("$..lastLogin"))
    );
}
XML
<test name="IgnorePathTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <receive endpoint="someEndpoint">
            <message type="json">
                <body>
                    <data>
                    {
                      "users":
                      [{
                        "name": "Jane",
                        "token": "?",
                        "lastLogin": 0
                      },
                      {
                        "name": "Penny",
                        "token": "?",
                        "lastLogin": 0
                      },
                      {
                        "name": "Mary",
                        "token": "?",
                        "lastLogin": 0
                      }]
                    }
                    </data>
                </body>
            </message>
            <ignore path="$..lastLogin" />
            <ignore path="$.users[*].token" />
        </receive>
    </actions>
</test>
YAML
name: IgnorePathTest
actions:
  - receive:
      endpoint: "someEndpoint"
      message:
        type: "json"
        body:
          data: |
            {
              "users":
              [{
                "name": "Jane",
                "token": "?",
                "lastLogin": 0
              },
              {
                "name": "Penny",
                "token": "?",
                "lastLogin": 0
              },
              {
                "name": "Mary",
                "token": "?",
                "lastLogin": 0
              }]
            }
          ignore:
            - path: "$.users[*].token"
            - path: "$..lastLogin"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="IgnorePathTest">
        <actions>
            <receive endpoint="someEndpoint">
                <message type="json">
                    <data>
                    {
                      "users":
                      [{
                        "name": "Jane",
                        "token": "?",
                        "lastLogin": 0
                      },
                      {
                        "name": "Penny",
                        "token": "?",
                        "lastLogin": 0
                      },
                      {
                        "name": "Mary",
                        "token": "?",
                        "lastLogin": 0
                      }]
                    }
                    </data>
                    <ignore path="$.users[*].token" />
                    <ignore path="$..lastLogin" />
                </message>
            </receive>
        </actions>
    </testcase>
</spring:beans>

The sample above adds JsonPath expressions as ignore statements. This means that we explicitly leave out the evaluated elements from validation. In the example above we explicitly skip the token entry and all lastLogin values that are obviously timestamp values in milliseconds.

The path evaluation is very powerful when it comes to select a set of objects and elements. This is how you can ignore several elements with path expressions.

As an alternative you can also use the @ignore@ placeholder in the message content. The placeholder tells Citrus to skip the element in validation.

Java
@CitrusTest
public void ignorePathTest() {
    $(receive(someEndpoint)
        .message()
        .type(MessageType.JSON)
        .body("""
            {
              "users":
              [{
                "name": "Jane",
                "token": "@ignore@",
                "lastLogin": "@ignore@"
              },
              {
                "name": "Penny",
                "token": "@ignore@",
                "lastLogin": "@ignore@"
              },
              {
                "name": "Mary",
                "token": "@ignore@",
                "lastLogin": "@ignore@"
              }]
            }
        """)
    );
}
XML
<test name="IgnorePathTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <receive endpoint="someEndpoint">
            <message type="json">
                <body>
                    <data>
                    {
                      "users":
                      [{
                        "name": "Jane",
                        "token": "@ignore@",
                        "lastLogin": "@ignore@"
                      },
                      {
                        "name": "Penny",
                        "token": "@ignore@",
                        "lastLogin": "@ignore@"
                      },
                      {
                        "name": "Mary",
                        "token": "@ignore@",
                        "lastLogin": "@ignore@"
                      }]
                    }
                    </data>
                </body>
            </message>
        </receive>
    </actions>
</test>
YAML
name: IgnorePathTest
actions:
  - receive:
      endpoint: "someEndpoint"
      message:
        type: "json"
        body:
          data: |
            {
              "users":
              [{
                "name": "Jane",
                "token": "@ignore@",
                "lastLogin": "@ignore@"
              },
              {
                "name": "Penny",
                "token": "@ignore@",
                "lastLogin": "@ignore@"
              },
              {
                "name": "Mary",
                "token": "@ignore@",
                "lastLogin": "@ignore@"
              }]
            }
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="IgnorePathTest">
        <actions>
            <receive endpoint="someEndpoint">
                <message type="json">
                  <data>
                    {
                      "users":
                      [{
                        "name": "Jane",
                        "token": "@ignore@",
                        "lastLogin": "@ignore@"
                      },
                      {
                        "name": "Penny",
                        "token": "@ignore@",
                        "lastLogin": "@ignore@"
                      },
                      {
                        "name": "Mary",
                        "token": "@ignore@",
                        "lastLogin": "@ignore@"
                      }]
                    }
                  </data>
                </message>
            </receive>
        </actions>
    </testcase>
</spring:beans>
You can also ignore a whole sub-tree in XML and whole objects and arrays in Json with the ignore expression/placeholder.
The ignore expression as well as the @ignore@ placeholder will only skip the value matching validations for the selected element or object. The element still has to be present in the message structure. In case the element is missing for any reason the validation fails even for ignored values.

8.2.4. 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.

Let’s 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 anymore 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:

Java
@CitrusTest
public void selectorTest() {
    Map<String, String> selectorMap = new HasMap<>();
    selectorMap.put("correlationId", "Cx1x123456789");
    selectorMap.put("operation", "getOrders");

    $(receive(someEndpoint)
        .selector(selectorMap)
        .message()
        .body("...")
    );
}
XML
<test name="SelectorTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <receive endpoint="someEndpoint">
            <selector>
                <element name="correlationId" value="Cx1x123456789"/>
                <element name="operation" value="getOrders"/>
            </selector>
            <message>
                <!-- -->
            </message>
        </receive>
    </actions>
</test>
YAML
name: SelectorTest
actions:
  - receive:
      endpoint: "someEndpoint"
      selector:
        element:
          - name: correlationId
            value: "Cx1x123456789"
          - name: operation
            value: "getOrders"
      message:
        # ...
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="SelectorTest">
        <actions>
            <receive endpoint="someEndpoint">
              <selector>
                <element name="correlationId" value="Cx1x123456789"/>
                <element name="operation" value="getOrders"/>
              </selector>
              <!-- ... -->
            </receive>
        </actions>
    </testcase>
</spring:beans>

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.

Java
@CitrusTest
public void selectorTest() {
    $(receive(someEndpoint)
        .selector("correlationId = 'Cx1x123456789' AND operation = 'getOrders'")
        .message()
        .body("...")
    );
}
XML
<test name="SelectorTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <receive endpoint="someEndpoint">
            <selector>
                <value>
                    correlationId = 'Cx1x123456789' AND operation = 'getOrders'
                </value>
            </selector>
            <message>
                <!-- -->
            </message>
        </receive>
    </actions>
</test>
YAML
name: SelectorTest
actions:
  - receive:
      endpoint: "someEndpoint"
      selector:
        value: "correlationId = 'Cx1x123456789' AND operation = 'getOrders'"
      message:
        # ...
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="SelectorTest">
        <actions>
            <receive endpoint="someEndpoint">
              <selector>
                <value>
                    correlationId = 'Cx1x123456789' AND operation = 'getOrders'
                </value>
              </selector>
              <!-- ... -->
            </receive>
        </actions>
    </testcase>
</spring:beans>
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.

8.2.5. Groovy Markup builder

With the Groovy markup builder you can build XML message body content in a simple way, without having to write the typical XML overhead.

The Groovy test action support lives in a separate module. You need to add the module to your project to use the functionality.
citrus-groovy dependency module
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-groovy</artifactId>
  <version>${citrus.version}</version>
</dependency>

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 body XML data we write a Groovy script snippet.

Java
@CitrusTest
public void scriptMessageBuilderTest() {
    $(receive("helloService")
        .message()
        .body(new GroovyScriptPayloadBuilder("""
                    markupBuilder.TestRequest(xmlns: 'https://citrus.schemas/samples/sayHello.xsd') {
                        Message('Hello World!')
                    }
        """))
    );
}
XML
<test name="ScriptMessageBuilderTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <receive endpoint="helloService">
            <message>
                <body>
                    <builder type="groovy">
                        markupBuilder.TestRequest(xmlns: 'https://citrus.schemas/samples/sayHello.xsd') {
                            Message('Hello World!')
                        }
                    </builder>
                </body>
            </message>
        </receive>
    </actions>
</test>
YAML
name: ScriptMessageBuilderTest
actions:
  - receive:
      endpoint: "helloService"
      message:
        builder:
          type: "groovy"
          value: |
            markupBuilder.TestRequest(xmlns: 'https://citrus.schemas/samples/sayHello.xsd') {
                Message('Hello World!')
            }
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="ScriptMessageBuilderTest">
        <actions>
            <receive endpoint="helloService">
              <message>
                <builder type="groovy">
                    markupBuilder.TestRequest(xmlns: 'https://citrus.schemas/samples/sayHello.xsd') {
                        Message('Hello World!')
                    }
                </builder>
              </message>
            </receive>
        </actions>
    </testcase>
</spring:beans>

The Groovy markup builder generates the XML message body with following content:

Generated markup
<TestRequest xmlns="https://citrus.schemas/samples/sayHello.xsd">
  <Message>Hello World</Message>
</TestRequest>

We use the builder element with type groovy and the markup builder 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 markup builder 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 markup builder extension:

  • The markup builder 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 markup builder script may also be used as external file resource:

Java
@CitrusTest
public void scriptMessageBuilderTest() {
    $(receive("helloService")
        .message()
        .body(new GroovyFileResourcePayloadBuilder("classpath:path/to/helloRequest.groovy"))
    );
}
XML
<test name="ScriptMessageBuilderTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <receive endpoint="helloService">
            <message>
                <body>
                    <builder type="groovy" file="classpath:path/to/helloRequest.groovy"/>
                </body>
            </message>
        </receive>
    </actions>
</test>
YAML
name: ScriptMessageBuilderTest
actions:
  - receive:
      endpoint: "helloService"
      message:
        builder:
          type: "groovy"
          file: "classpath:path/to/helloRequest.groovy"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="ScriptMessageBuilderTest">
        <actions>
            <receive endpoint="helloService">
              <message>
                <builder type="groovy" file="classpath:path/to/helloRequest.groovy"/>
              </message>
            </receive>
        </actions>
    </testcase>
</spring:beans>

The markup builder implementation in Groovy offers great possibilities in defining message body content. We do not need to write XML the tag overhead anymore. The approach also enables us to construct complex message body content with Groovy script logic like iterations and conditional elements. For detailed markup builder descriptions please see the official Groovy documentation.

8.3. SQL

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.

The SQL test actions live in a separate module. You need to add the module to your project to use the actions.
citrus-sql dependency module
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-sql</artifactId>
  <version>${citrus.version}</version>
</dependency>

In general Citrus handles SELECT statements differently to other statements like INSERT, UPDATE and DELETE. When executing an 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.

8.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.

Java
@Autowired
@Qualifier("myDataSource")
private DataSource dataSource;

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

    $(sql()
        .dataSource(dataSource)
        .sqlResource("file:tests/unit/resources/script.sql"))
    );
}
XML
<test name="SQLTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <sql datasource="myDataSource">
            <statements>
                <statement>DELETE FROM CUSTOMERS</statement>
                <statement>DELETE FROM ORDERS</statement>
            </statements>
        </sql>

        <sql datasource="myDataSource">
            <statements file="file:tests/unit/resources/script.sql"/>
        </sql>
    </actions>
</test>
YAML
name: "SQLTest"
actions:
  - sql:
      dataSource: "myDataSource"
      statements:
        - statement: "DELETE FROM CUSTOMERS"
        - statement: "DELETE FROM ORDERS"

  - sql:
      dataSource: "myDataSource"
      statements:
        - file: "file:tests/unit/resources/script.sql"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="SQLTest">
        <actions>
            <sql datasource="myDataSource">
                <statement>DELETE FROM CUSTOMERS</statement>
                <statement>DELETE FROM ORDERS</statement>
            </sql>

            <sql datasource="myDataSource">
                <resource file="file:tests/unit/resources/script.sql"/>
            </sql>
        </actions>
    </testcase>
</spring:beans>

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.

8.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.

Java
@Autowired
@Qualifier("myDataSource")
private DataSource dataSource;

@CitrusTest
public void sqlQueryTest() {
    $(sql()
        .dataSource(dataSource)
        .query()
            .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"));
}
XML
<test name="SqlQueryTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <sql datasource="myDataSource">
            <statements>
                <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>
            </statements>
            <validate column="ID" value="1"/>
            <validate column="NAME" value="Christoph"/>
            <validate column="COUNT(*)" value="${rowsCount}"/>
            <validate column="DESCRIPTION" value="null"/>
        </sql>
    </actions>
</test>
YAML
name: "SqlQueryTest"
actions:
  - sql:
      dataSource: "dataSource"
      statements:
        - 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:
        - column: "ID"
          value: "1"
        - column: "NAME"
          value: "Christoph"
        - column: "COUNT(*)"
          value: '${rowsCount}'
        - column: "DESCRIPTION"
          value: "null"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <!-- ... -->
    <sql datasource="myDataSource">
        <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>
</spring:beans>

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 can pass a variable argument list to the validate method like this:
Java
$(sql()
    .query()
    .statement("...")
    .validate("SOME_COLUMN",
        "Value in 1st row",
        "Value in 2nd row",
        "Value in 3rd row",
        "Value in x row")
);
XML
<sql datasource="myDataSource">
    <statements>
        <statement>...</statement>
    </statement>
    <validate column="SOME_COLUMN">
      <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>
</sql>
YAML
- sql:
  dataSource: myDataSource
  statements:
    - statement: {}
  validate:
    - column: "SOME_COLUMN"
      values:
        - "Value in 1st row"
        - "Value in 2nd row"
        - "Value in 3rd row"
        - "Value in x row"
Spring XML
<sql dataSource="myDataSource">
    <statement>...</statement>
    <validate column="SOME_COLUMN">
      <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>
</sql>

Next example shows how to work with multiple row result sets and multiple values to expect within one column:

Java
$(sql()
    .dataSource(myDataSource)
    .query()
    .statement("select WEEKDAY as DAY, DESCRIPTION from WEEK")
    .validate("DAY",
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "@ignore@",
        "@ignore@")
    .validate("DESCRIPTION",
        "I hate Mondays!",
        "Tuesday is sports day",
        "The mid of the week",
        "Thursday we play chess",
        "Friday, the weekend is near!",
        "@ignore@",
        "@ignore@")
);
XML
<sql datasource="myDataSource">
    <statements>
        <statement>select WEEKDAY as DAY, DESCRIPTION from WEEK</statement>
    </statements>
    <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>
YAML
- sql:
  dataSource: "myDataSource"
  statements:
    - statement: "select WEEKDAY as DAY, DESCRIPTION from WEEK"
  validate:
    - column: "SOME_COLUMN"
      values:
        - "Value in 1st row"
        - "Value in 2nd row"
        - "Value in 3rd row"
        - "Value in x row"
Spring XML
<sql datasource="myDataSource">
    <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.

8.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.

Java
$(sql()
    .dataSource(myDataSource)
    .transactionManager(transactionManager)
    .transactionTimeout(15000)
    .transactionIsolationLevel("ISOLATION_READ_COMMITTED")
    .statement("DELETE FROM CUSTOMERS")
    .statement("DELETE FROM ORDERS")
);
XML
<sql datasource="someDataSource">
    <transaction>
        <manager>"someTransactionManager"</manager>
        <timeout>"15000"</timeout>
        <isolation-level>"ISOLATION_READ_COMMITTED"</isolation-level>
    </transaction>
    <statements>
        <statement>DELETE FROM CUSTOMERS</statement>
        <statement>DELETE FROM ORDERS</statement>
    </statements>
</sql>
YAML
- sql:
    datasource: "myDataSource"
    transaction:
      manager: "someTransactionManager"
      timeout: 15000
      isolation-level: "ISOLATION_READ_COMMITTED"
    statements:
      - statement: "DELETE FROM CUSTOMERS"
      - statement: "DELETE FROM ORDERS"
Spring XML
<sql datasource="myDataSource"
     transaction-manager="someTransactionManager"
     transaction-timeout="15000"
     transaction-isolation-level="ISOLATION_READ_COMMITTED">
    <statement>DELETE FROM CUSTOMERS</statement>
    <statement>DELETE FROM ORDERS</statement>
</sql>

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:

Spring Bean
@Bean
public TransactionManager myTransactionManager(DataSource myDataSource) {
    return new DataSourceTransactionManager(myDataSource);
}
Spring XML
<bean id="myTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <constructor-arg ref="myDataSource"/>
</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

8.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.

Java
$(sql()
    .dataSource(myDataSource)
    .query()
    .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")
);
XML
<sql datasource="myDataSource">
    <statements>
        <statement>select ID from CUSTOMERS where NAME='${customerName}'</statement>
        <statement>select ORDERTYPE, STATUS from ORDERS where ID='${orderId}'</statement>
    </statements>
    <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']
        </script>
    </validate>
</sql>
YAML
- sql:
  dataSource: "myDataSource"
  statements:
    - statement: "select ID from CUSTOMERS where NAME='${customerName}'"
    - statement: "select ORDERTYPE, STATUS from ORDERS where ID='${orderId}'"
  validate:
    - script:
        type: "groovy"
        value: |
          assert rows.size() == 2
          assert rows[0].ID == '1'
          assert rows[1].STATUS == 'in progress'
          assert rows[1] == [ORDERTYPE:'SampleOrder', STATUS:'in progress']
Spring XML
<sql datasource="myDataSource">
    <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>

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.

8.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:

Java
$(sql()
    .dataSource(myDataSource)
    .query()
        .statement("select ID from CUSTOMERS where NAME='${customerName}'")
        .statement("select STATUS from ORDERS where ID='${orderId}'")
    .extract("ID", "customerId")
    .extract("STATUS", "orderStatus")
);
XML
<sql datasource="myDataSource">
    <statements>
        <statement>select ID from CUSTOMERS where NAME='${customerName}'</statement>
        <statement>select STATUS from ORDERS where ID='${orderId}'</statement>
    </statements>
    <extract  column="ID" variable="customerId"/>
    <extract  column="STATUS" variable="orderStatus"/>
</sql>
YAML
- sql:
  dataSource: "myDataSource"
  statements:
    - statement: "select ID from CUSTOMERS where NAME='${customerName}'"
    - statement: "select ORDERTYPE, STATUS from ORDERS where ID='${orderId}'"
  extract:
    - column: "ID"
      variable: "customerId"
    - column: "STATUS"
      variable: "orderStatus"
Spring XML
<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>

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.

8.4. Sleep

This action shows how to make the test framework execution stop 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.

Java
@CitrusTest
public void sleepTest() {
    $(sleep().seconds(3.5));

    $(sleep().milliseconds(500));

    $(sleep());
}
XML
<test name="SleepTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <sleep seconds="3.5"/>

        <sleep milliseconds="500"/>

        <sleep/>
    </actions>
</test>
YAML
name: SleepTest
actions:
  - sleep:
      seconds: "3.5"
  - sleep:
      milliseconds: "500"
  - sleep: {}
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="SleepTest">
        <actions>
            <sleep seconds="3.5"/>

            <sleep milliseconds="500"/>

            <sleep/>
        </actions>
    </testcase>
</spring:beans>

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.

8.5. Delay

This action shows how to make the test framework execution stop 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.

Java
@CitrusTest
public void delayTest() {
    $(delay().seconds(3.5));

    $(delay().milliseconds(500));

    $(delay());
}
XML
<test name="DelayTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <delay seconds="3.5"/>

        <delay milliseconds="500"/>

        <delay/>
    </actions>
</test>
YAML
name: DelayTest
actions:
  - delay:
      seconds: "3.5"
  - delay:
      milliseconds: "500"
  - delay: {}
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="DelayTest">
        <actions>
            <delay seconds="3.5"/>

            <delay milliseconds="500"/>

            <delay/>
        </actions>
    </testcase>
</spring:beans>

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.

8.6. 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
@CitrusTest
public void javaActionTest() {
    $(java("org.citrusframework.test.util.InvocationDummy")
        .constructorArgs("Test Invocation")
        .method("invoke")
        .methodArgs(new String[] { "1","2" })
    );

    $(java(new InvocationDummy("Test Invocation"))
        .method("invoke")
        .methodArgs(new Object[] { 4, "Test Invocation", true })
    );

    $(java(InvocationDummy.class)
        .method("main")
        .methodArgs(new String[] { "4", "Test Invocation", "true" })
    );
}
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="JavaTest">
        <actions>
            <java class="org.citrusframework.test.util.InvocationDummy">
                <constructor>
                    <argument type="">Test Invocation</argument>
                </constructor>
                <method name="invoke">
                    <argument type="String[]">1,2</argument>
                </method>
            </java>

            <java class="org.citrusframework.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="org.citrusframework.test.util.InvocationDummy">
                <method name="main">
                    <argument type="String[]">4,Test,true </argument>
                </method>
            </java>
        </actions>
    </testcase>
</spring:beans>

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
InvocationDummy invocationDummy = new InvocationDummy("Test Invocation");
$(java(invocationDummy)
    .method("invoke")
    .methodArgs(new Object[] { 4, "Test Invocation", true })
);
Spring XML
<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="org.citrusframework.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.

8.7. Expect 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:

Java
@Autowired
@Qualifier("myEndpoint")
private Endpoint myEndpoint;

@CitrusTest
public void expectTimeoutTest() {
    $(expectTimeout()
            .endpoint(myEndpoint)
            .timeout(500)
    );
}
XML
<test name="ExpectTimeoutTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <expect-timeout endpoint="myEndpoint" wait="500"/>
    </actions>
</test>
YAML
name: ExpectTimeoutTest
actions:
  - expect-timeout:
      endpoint: "myEndpoint"
      wait: "500"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="ExpectTimeoutTest">
        <actions>
            <expect-timeout endpoint="myEndpoint" wait="500"/>
        </actions>
    </testcase>
</spring:beans>

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.

Java
@Autowired
@Qualifier("myEndpoint")
private Endpoint myEndpoint;

@CitrusTest
public void expectTimeoutTest() {
    $(expectTimeout()
            .endpoint(myEndpoint)
            .timeout(500)
            .selector("MessageId = '123456789'")
    );
}
XML
<test name="ExpectTimeoutTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <expect-timeout endpoint="myEndpoint" wait="500">
            <selector>MessageId='123456789'</selector>
        </expect-timeout>
    </actions>
</test>
YAML
name: ExpectTimeoutTest
actions:
  - expect-timeout:
      endpoint: "myEndpoint"
      wait: "500"
      selector:
        value: "MessageId='123456789'"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="ExpectTimeoutTest">
        <actions>
            <expect-timeout endpoint="myEndpoint" wait="500">
                <select>MessageId='123456789'<select/>
            </expect-timeout>
        </actions>
    </testcase>
</spring:beans>

8.8. 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 in the next code example:

Java
@CitrusTest
public void echoTest() {
    variable("date", "citrus:currentDate()");

    $(echo().message("Hello Test Framework"));
    $(echo().message("Current date is: ${date}"));
}
XML
<test name="EchoTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <variables>
        <variable name="today" value="citrus:currentDate()"/>
    </variables>
    <actions>
        <echo>
            <message>Hello Test Framework</message>
        </echo>

        <echo>
            <message>Current date is: ${date}</message>
        </echo>
    </actions>
</test>
YAML
name: EchoTest
variables:
  - name: "today"
    value: "citrus:currentDate()"
actions:
  - echo:
      message: "Hello Test Framework"
  - echo:
      message: "Current date is: ${date}"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="EchoTest">
        <variables>
            <variable name="today" value="citrus:currentDate()"/>
        </variables>
        <actions>
            <echo>
                <message>Hello Test Framework</message>
            </echo>

            <echo>
                <message>Current date is: ${today}</message>
            </echo>
        </actions>
    </testcase>
</spring:beans>

Result on the console:

Hello Test Framework
Current time is: 05.08.2008

8.9. Print

The <print> 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:

Java
@CitrusTest
public void printTest() {
    variable("date", "citrus:currentDate()");

    $(print().message("Hello Test Framework"));
    $(print().message("Current date is: ${date}"));
}
XML
<test name="PrintTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <variables>
        <variable name="today" value="citrus:currentDate()"/>
    </variables>
    <actions>
        <print>
            <message>Hello Test Framework</message>
        </print>

        <print>
            <message>Current date is: ${date}</message>
        </print>
    </actions>
</test>
YAML
name: PrintTest
variables:
  - name: "today"
    value: "citrus:currentDate()"
actions:
  - print:
      message: "Hello Test Framework"
  - print:
      message: "Current date is: ${date}"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="PrintTest">
        <variables>
            <variable name="today" value="citrus:currentDate()"/>
        </variables>
        <actions>
            <print>
                <message>Hello Test Framework</message>
            </print>

            <print>
                <message>Current date is: ${today}</message>
            </print>
        </actions>
    </testcase>
</spring:beans>

Result on the console:

Hello Test Framework
Current time is: 05.08.2008

8.10. Stop time

Time measurement during a test can be very helpful. The <trace-time> action creates and monitors multiple timelines. The action offers the attribute id to identify a timeline. The tester can of course use more than one timeline with different ids simultaneously.

Read the next example, and you will understand the mix of different timelines:

Java
@CitrusTest
public void sleepTest() {
    $(stopTime());

    $(stopTime().id("time_line_id"));

    $(sleep().seconds(3.5));

    $(stopTime().id(" time_line_id "));

    $(sleep().milliseconds(5000));

    $(stopTime());

    $(stopTime().id(" time_line_id "));

}
XML
<test name="SleepTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <stop-time/>

        <stop-time id="time_line_id"/>

        <sleep seconds="3.5"/>

        <stop-time id=" time_line_id "/>

        <sleep milliseconds="5000"/>

        <stop-time/>

        <stop-time id=" time_line_id "/>
    </actions>
</test>
YAML
name: SleepTest
actions:
  - stop-time: {}

  - stop-time:
      id: "time_line_id"

  - sleep:
      seconds: "3.5"

  - stop-time:
      id: "time_line_id"

  - sleep:
      milliseconds: "5000"

  - stop-time: {}

  - stop-time:
      id: "time_line_id"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="SleepTest">
        <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>
</spring:beans>

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
Timeline ids should not exist as test variables before the action is called for the first time. This would break the timeline initialization.
In case no timeline id is specified the framework will measure the time for a default timeline. To print out the current elapsed time for a timeline you simply have to place the `<trace-time> action into the action chain again and again, using the respective timeline identifier. The elapsed time will be printed out to the console every time.

Each timeline is stored as test variable in the test case. By default you will have the following test variables set for each timeline:

CITRUS_TIMELINE

first timestamp of timeline

CITRUS_TIMELINE_VALUE

latest time measurement value (time passed since first timestamp in milliseconds)

According to your timeline id you will get different test variable names. Also you can customize the time value suffix (default: _VALUE):

Java
@CitrusTest
public void sleepTest() {
    $(stopTime().id("custom_watcher").suffix("_1st"));

    $(sleep());

    $(stopTime().id(" custom_watcher ").suffix("_2nd"));
}
XML
<test name="SleepTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <stop-time id="custom_watcher" suffix="_1st"/>

        <sleep/>

        <stop-time id="custom_watcher" suffix="_2nd"/>
    </actions>
</test>
YAML
name: SleepTest
actions:
  - stop-time:
      id: "custom_watcher"
      suffix: "_1st"

  - sleep: {}

  - stop-time:
      id: "custom_watcher"
      suffix: "_2nd"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="SleepTest">
        <actions>
            <trace-time id="custom_watcher" suffix="_1st"/>

            <sleep/>

            <trace-time id="custom_watcher" suffix="_2nd"/>
        </actions>
    </testcase>
</spring:beans>

You will get following test variables set:

custom_watcher

first timestamp of timeline

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.

8.11. 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.

Java
@CitrusTest
public void createVariableTest() {
    variable("myVariable", "12345");
    variable("id", "54321");

    $(echo()
        .message("Current variable value: ${myVariable}"));

    $(createVariable("myVariable", "${id}"));
    $(createVariable("newVariable", "this is a test"));

    $(echo()
        .message("Current variable value: ${myVariable}"));

    $(echo()
        .message("New variable 'newVariable' has the value: ${newVariable}"));
}
XML
<test name="CreateVariablesTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <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>
</test>
YAML
name: "CreateVariablesTest"
variables:
  - name: "myVariable"
    value: "12345"
  - name: "id"
    value: "54321"
actions:
  - echo:
      message: "Current variable value: ${myVariable}"
  - create-variable:
      variables:
        - name: "myVariable"
          value: "${id}"
        - name: "newVariable"
          value: "this is a test"
  - echo:
      message: "Current variable value: ${myVariable}"
  - echo:
      message: "New variable 'newVariable' has the value: ${newVariable}"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <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>
</spring:beans>
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.

8.12. 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:

Java
@CitrusTest
public void traceVariablesTest() {
    variable("myVariable", "12345");
    variable("nextVariable", "54321");

    $(traceVariables("myVariable", "nextVariable"));
    $(traceVariables());
}
XML
<test name="TraceVariablesTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <variables>
        <variable name="myVariable" value="12345"/>
        <variable name="nextVariable" value="54321"/>
    </variables>
    <actions>
        <trace>
            <variable name="myVariable"/>
            <variable name="nextVariable"/>
        </trace>

        <trace-variables/>
    </actions>
</test>
YAML
name: TraceVariablesTest
variables:
  - name: "myVariable"
    value: "12345"
  - name: "nextVariable"
    value: "54321"
actions:
  - trace:
      variables:
          - name: "myVariable"
          - name: "nextVariable"
  - trace: {}
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <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>
</spring:beans>

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

8.13. 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:

Java
@CitrusTest
public void transformTest() {
    $(transform()
        .variable("result")
        .source("""
            <TestRequest>
                <Message>Hello World!</Message>
            </TestRequest>
        """)
        .xslt("""
            <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>
        """)
    );

    $(echo().message("${result}"));
}
XML
<test name="TransformTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <transform variable="result">
            <source>
              <![CDATA[
                  <TestRequest>
                      <Message>Hello World!</Message>
                  </TestRequest>
              ]]>
            </source>
            <xslt>
              <![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>
        </transform>
        <echo>
          <message>${result}</message>
        </echo>
    </actions>
</test>
YAML
name: TransformTest
actions:
  - transform:
      variable: result
      source:
        value: |
          <TestRequest>
              <Message>Hello World!</Message>
          </TestRequest>
      xslt:
        value: |
          <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>
  - echo:
      message: '${result}'
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <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>
</spring:beans>

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:

Java
@CitrusTest
public void transformTest() {
    $(transform()
        .variable("result")
        .source(new ClassPathResource("org/citrusframework/actions/transform-source.xml"))
        .xslt(new ClassPathResource("org/citrusframework/actions/transform.xslt"))
    );

    $(echo().message("${result}"));
}
XML
<test name="TransformTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <transform variable="result">
            <source file="classpath:transform-source.xml"/>
            <xslt file="classpath:transform.xslt"/>
        </transform>
        <echo>
          <message>${result}</message>
        </echo>
    </actions>
</test>
YAML
name: TransformTest
actions:
  - transform:
      variable: result
      source:
        file: "classpath:transform-source.xml"
      xslt:
        file: "classpath:transform.xslt"
  - echo:
      message: '${result}'
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="TransformTest">
        <actions>
            <transform variable="result">
                <xml-resource file="classpath:transform-source.xml"/>
                <xslt-resource file="classpath:transform.xslt"/>
            </transform>
            <echo>
              <message>${result}</message>
            </echo>
        </actions>
    </testcase>
</spring:beans>

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.

8.14. 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 Groovy test action support lives in a separate module. You need to add the module to your project to use the functionality.
citrus-groovy dependency module
<dependency>
  <groupId>org.citrusframework</groupId>
  <artifactId>citrus-groovy</artifactId>
  <version>${citrus.version}</version>
</dependency>

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:

Java
@CitrusTest
public void GroovyTest() {
    $(groovy()
        .script("println 'Hello Citrus'")
    );

    $(groovy()
        .script("println 'The variable is: ${time}'")
    );

    $(groovy()
        .scriptResourcePath("classpath:org/citrusframework/script/example.groovy")
    );
}
XML
<test name="GroovyTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <variables>
        <variable name="time" value="citrus:currentDate()"/>
    </variables>
    <actions>
        <groovy>
            println 'Hello Citrus'
        </groovy>
        <groovy>
            println 'The variable is: ${time}'
        </groovy>
        <groovy file="classpath:org/citrusframework/script/example.groovy"/>
    </actions>
</test>
YAML
name: GroovyTest
variables:
  - name: "time"
    value: "citrus:currentDate()"
actions:
  - groovy:
      script: "println 'Hello Citrus'"
  - groovy:
      script: |
        println 'The variable is: ${time}'
  - groovy:
      file: classpath:org/citrusframework/script/example.groovy
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <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:org/citrusframework/script/example.groovy"/>
      </actions>
    </testcase>
</spring:beans>

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).

Java
@CitrusTest
public void GroovyTest() {
    $(groovy()
        .script("""
            context.setVariable("greetingText","Hello Citrus")
            println context.getVariable("greetingText")
        """)
    );

    $(echo()
        .message("New variable: ${greetingText}")
    );
}
XML
<test name="GroovyTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <groovy>
            context.setVariable("greetingText","Hello Citrus")
            println context.getVariable("greetingText")
        </groovy>
        <echo>
            <message>New variable: ${greetingText}</message>
        </echo>
    </actions>
</test>
YAML
name: GroovyTest
actions:
  - groovy:
      script: |
        context.setVariable("greetingText","Hello Citrus")
        println context.getVariable("greetingText")
  - echo:
      message: "New variable: ${greetingText}"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="groovyTest">
      <actions>
        <groovy>
          context.setVariable("greetingText","Hello Citrus")
          println context.getVariable("greetingText")
        </groovy>

        <echo>
          <message>New variable: ${greetingText}</message>
        </echo>
      </actions>
    </testcase>
</spring:beans>
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:
Groovy script template
import org.citrusframework.*
import org.citrusframework.variable.*
import org.citrusframework.context.TestContext
import org.citrusframework.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:

Java
$(groovy()
    .template("classpath:my-custom-template.groovy")
    .script("...")
);
XML
<groovy script-template="classpath:my-custom-template.groovy">
    <!-- ... -->
</groovy>
YAML
- groovy:
  script-template: "classpath:my-custom-template.groovy"
  script: "..."
Spring XML
<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:

Java
$(groovy()
    .useScriptTemplate(false)
    .script("println 'Just use some Groovy code'")
);
XML
<groovy use-script-template="false">
    println 'Just use some Groovy code'
</groovy>
YAML
- groovy:
  use-script-template: false
  script: "println 'Just use some Groovy code'"
Spring XML
<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.

Java
@CitrusTest
public void GroovyTest() {
    variable("time", "citrus:currentDate()");

    $(groovy()
        .script("""
            import org.citrusframework.*
            import org.citrusframework.variable.*
            import org.citrusframework.context.TestContext
            import org.citrusframework.script.GroovyAction.ScriptExecutor

            public class GScript implements ScriptExecutor {
                public void execute(TestContext context) {
                    println context.getVariable("time")
                }
            }
        """)
    );
}
XML
<test name="GroovyTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <variables>
        <variable name="time" value="citrus:currentDate()"/>
    </variables>
    <actions>
        <groovy>
        <![CDATA[
            import org.citrusframework.*
            import org.citrusframework.variable.*
            import org.citrusframework.context.TestContext
            import org.citrusframework.script.GroovyAction.ScriptExecutor

            public class GScript implements ScriptExecutor {
                public void execute(TestContext context) {
                    println context.getVariable("time")
                }
            }
        ]]>
        </groovy>
    </actions>
</test>
YAML
name: GroovyTest
variables:
  - name: time
    value: citrus:currentDate()
actions:
  - groovy:
      script: |
        import org.citrusframework.*
        import org.citrusframework.variable.*
        import org.citrusframework.context.TestContext
        import org.citrusframework.script.GroovyAction.ScriptExecutor

        public class GScript implements ScriptExecutor {
            public void execute(TestContext context) {
                println context.getVariable("time")
            }
        }
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="groovyTest">
      <variables>
        <variable name="time" value="citrus:currentDate()"/>
      </variables>
      <actions>
        <groovy>
          <![CDATA[
            import org.citrusframework.*
            import org.citrusframework.variable.*
            import org.citrusframework.context.TestContext
            import org.citrusframework.script.GroovyAction.ScriptExecutor

            public class GScript implements ScriptExecutor {
                public void execute(TestContext context) {
                    println context.getVariable("time")
                }
            }
          ]]>
        </groovy>
      </actions>
    </testcase>
</spring:beans>

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.

8.15. 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:

Java
@CitrusTest
public void failTest() {
    $(fail().message("Test will fail with custom message"));
}
XML
<test name="FailTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <fail message="Test will fail with custom message"/>
    </actions>
</test>
YAML
name: FailTest
actions:
  - fail:
      message: "Test will fail with custom message"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="FailTest">
        <actions>
            <fail message="Test will fail with custom message"/>
        </actions>
    </testcase>
</spring:beans>

Test results:

Execution of test: failTest failed! Nested exception is:
org.citrusframework.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 you can also raise arbitrary Java exceptions and let the test fail.

Java
@CitrusTest
public void throwExceptionSample() {
    // some test actions

    throw new ValidationException("This test should fail now");
}

The validation exception above will cause the test to fail as expected. However you may consider using the fail action to raise errors because this ensures to properly report the error in the Citrus test reports.

8.16. 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.

Java
@CitrusTest
public void inputActionTest() {
    variable("userinput", "");
    variable("userinput1", "");
    variable("userinput2", "y");
    variable("userinput3", "yes");
    variable("userinput4", "");

    $(input());

    $(echo().message("user input was: ${userinput}"));

    $(input().message("Now press enter:").result("userinput1"));

    $(echo().message("user input was: ${userinput1}"));

    $(input().message("Do you want to continue?").answers("y", "n").result("userinput2"));

    $(echo().message("user input was: ${userinput2}"));

    $(input().message("Do you want to continue?").answers("yes", "no").result("userinput3"));

    $(echo().message("user input was: ${userinput3}"));

    $(input().result("userinput4"));

    $(echo().message("user input was: ${userinput4}"));
}
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <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>
</spring:beans>

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})

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.

8.17. Load Properties

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
Java
@CitrusTest
public void loadPropertiesTest() {
    $(load().filePath("file:tests/resources/load.properties"));
}
XML
<test name="LoadPropertiesTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <load>
            <properties file="file:tests/resources/load.properties"/>
        </load>
    </actions>
</test>
YAML
name: LoadPropertiesTest
actions:
  - load:
      properties:
        file: "file:tests/resources/load.properties"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="LoadPropertiesTest">
        <actions>
            <load>
                <properties file="file:tests/resources/load.properties"/>
            </load>
        </actions>
    </testcase>
</spring:beans>
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!

8.18. 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.

Java
@Autowired
@Qualifier("connectionFactory")
private ConnectionFactory connectionFactory;

@CitrusTest
public void purgeJmsQueuesTest() {
    $(purgeQueues()
        .connectionFactory(connectionFactory)
        .queue("JMS.Queue.1")
        .queue("JMS.Queue.2")
        .queue("JMS.Queue.3")
    );
}
XML
<test name="PurgeJmsQueuesTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <purge-jms-queues connection-factory="connectionFactory">
            <queue name="JMS.Queue.1"/>
            <queue name="JMS.Queue.2"/>
            <queue name="JMS.Queue.3"/>
        </purge-jms-queues>
    </actions>
</test>
YAML
name: PurgeJmsQueuesTest
actions:
  - purge-queues:
      connection-factory: "connectionFactory"
      queues:
        - "JMS.Queue.1"
        - "JMS.Queue.2"
        - "JMS.Queue.3"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="purgeJmsQueuesTest">
      <actions>
          <jms:purge-jms-queues connection-factory="connectionFactory">
              <jms:queue name="JMS.Queue.1"/>
              <jms:queue name="JMS.Queue.2"/>
              <jms:queue name="JMS.Queue.3"/>
          </jms:purge-jms-queues>
      </actions>
    </testcase>
</spring:beans>
Citrus provides special support for JMS related features when using the Spring XML bean integration. You 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 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>

Notice that we have referenced the jms namespace when using the purge-jms-queues test action.

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 these tasks a lot. The test suite offers a very simple way to purge the destinations between the tests. See testsuite-before-test for 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.

Spring Bean
@Bean
public SequenceBeforeTest beforeTest() {
    return SequenceBeforeTest.Builder.beforeTest()
            .actions(
                purgeQueues()
                    .queue("fooChannel")
                    .queue("barChannel")
            );
}
Spring XML
<spring:beans xmlns:spring="http://www.springframework.org/schema/beans"
              xmlns:citrus="http://www.citrusframework.org/schema/config">
    <citrus:before-test id="purgeBeforeTest">
        <citrus:actions>
            <jms:purge-jms-queues>
                <jms:queue name="fooChannel"/>
                <jms:queue name="barChannel"/>
            </jms:purge-jms-queues>
        </citrus:actions>
    </citrus:before-test>
</spring:beans>
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:

Spring XML
<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.

Java
@Autowired
@Qualifier("jmsQueue1")
private Queue jmsQueue1;

@Autowired
@Qualifier("jmsQueue2")
private Queue jmsQueue2;

@CitrusTest
public void purgeJmsQueuesTest() {
    $(purgeQueues()
        .queue(jmsQueue1)
        .queue(jmsQueue1)
    );
}
XML
<test name="PurgeJmsQueuesTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <purge-jms-queues>
            <queue ref="jmsQueue1"/>
            <queue ref="jmsQueue2"/>
        </purge-jms-queues>
    </actions>
</test>
YAML
name: PurgeJmsQueuesTest
actions:
  - purge-queues:
      queue: jmsQueue1
  - purge-queues:
      queue: jmsQueue2
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="purgeJmsQueuesTest">
      <actions>
          <jms:purge-jms-queues>
              <jms:queue ref="jmsQueue1"/>
              <jms:queue ref="jmsQueue2"/>
          </jms:purge-jms-queues>
      </actions>
    </testcase>
</spring:beans>

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.

You can mix queue name and queue object references as you like within one single purge queue test action.

8.19. Purging Spring Message Channels

The Spring Integration project defines message channels as a central messaging destination. These are in-memory message queues holding messages for test cases. Messages that are queued on a channel 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 avoid bad influence on 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:

Java
@Autowired
@Qualifier("someChannelName")
private MessageChannel someChannel;

@Autowired
@Qualifier("anotherChannelName")
private MessageChannel anotherChannel;

@CitrusTest
public void purgeChannelTest() {
    $(purgeChannels()
        .channelResolver(channelResolver)
        .channelNames("someChannelName", "anotherChannelName")
    );

    $(purgeChannels()
        .channels(someChannel, anotherChannel)
    );
}
XML
<test name="PurgeChannelTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <purge-channels channel-resolver="channelResolver">
            <channel name="someChannelName"/>
            <channel name="anotherChannelName"/>
        </purge-channels>

        <purge-channels>
            <channel ref="someChannel"/>
            <channel ref="anotherChannel"/>
        </purge-channels>
    </actions>
</test>
YAML
name: PurgeChannelTest
actions:
  - purge-channels:
      channel-resolver: "channelResolver"
      channels:
        - "someChannelName"
        - "anotherChannelName"
  - purge-channels:
      channel: someChannel
  - purge-channels:
      channel: anotherChannel
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <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>
</spring:beans>

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 bean id or name in your application context.

The channel resolver reference is optional and points to a channel resolver that is used to resolve names to actual channel instances. 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.

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:

Message selector implementation
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.

Spring Bean
@Bean
public MessageSelector specialMessageSelector() {
    return new TimeBasedMessageSelector();
}
Spring XML
<bean id="specialMessageSelector"
    class="org.citrusframework.special.TimeBasedMessageSelector"/>

Now let us have a look at how you reference the selector in your test case:

Java
@CitrusTest
public void purgeChannelTest() {
    $(purgeChannels()
        .selector(specialMessageSelector)
        .channels(someChannel, anotherChannel)
    );
}
XML
<test name="PurgeChannelTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <purge-channels message-selector="specialMessageSelector">
            <channel ref="someChannel"/>
            <channel ref="anotherChannel"/>
        </purge-channels>
    </actions>
</test>
YAML
name: PurgeChannelTest
actions:
  - purge-channels:
      message-selector: "specialMessageSelector"
      channels:
        - "someChannel"
        - "anotherChannel"
Spring XML
<purge-channel message-selector="specialMessageSelector">
  <channel ref="someChannel"/>
  <channel red="anotherChannel"/>
</purge-channel>

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.

Spring Bean
@Bean
public SequenceBeforeTest beforeTest() {
    return SequenceBeforeTest.Builder.beforeTest()
            .actions(
                purgeChannels()
                    .channel("fooChannel")
                    .channel("barChannel")
            );
}
Spring XML
<spring:beans xmlns:spring="http://www.springframework.org/schema/beans"
              xmlns:citrus="http://www.citrusframework.org/schema/config">
    <citrus:before-test id="purgeBeforeTest">
        <citrus:actions>
            <purge-channel>
                <channel name="fooChannel"/>
                <channel name="barChannel"/>
            </purge-channel>
        </citrus:actions>
    </citrus:before-test>
</spring:beans>

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 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.

8.20. 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. The enqueued 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:

Java
@Autowired
@Qualifier("someEndpointName")
private DirectEndpoint someEndpoint;

@Autowired
@Qualifier("anotherEndpointName")
private DirectEndpoint anotherEndpoint;

@CitrusTest
public void purgeEndpointTest() {
    $(purgeEndpoints()
        .endpointNames("someEndpointName", "anotherEndpointName")
    );

    $(purgeEndpoints()
        .endpoints(someEndpoint, anotherEndpoint)
    );
}
XML
<test name="PurgeEndpointTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <purge-endpoint>
            <endpoint name="someChannelName"/>
            <endpoint name="anotherChannelName"/>
        </purge-endpoint>

        <purge-endpoint>
            <endpoint ref="someChannel"/>
            <endpoint ref="anotherChannel"/>
        </purge-endpoint>
    </actions>
</test>
YAML
name: PurgeEndpointTest
actions:
  - purge:
      endpoints:
        - name: "someEndpointName"
        - name: "anotherEndpointName"
  - purge:
      endpoints:
        - ref: someEndpoint
        - ref: anotherEndpoint
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <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>
</spring:beans>

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.

When using the Java DSL we can inject endpoint objects with Spring bean container IoC.

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:

Java
@CitrusTest
public void purgeEndpointTest() {
    $(purgeEndpoints()
        .selector("operation = 'sayHello'")
        .endpoints(someEndpoint, anotherEndpoint)
    );
}
XML
<test name="PurgeEndpointTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <purge-endpoint>
            <selector>
                <value>operation = 'sayHello'</value>
            </selector>
            <endpoint ref="someEndpoint"/>
            <endpoint ref="anotherEndpoint"/>
        </purge-endpoint>
    </actions>
</test>
YAML
name: PurgeEndpointTest
actions:
  - purge-endpoints:
      selector:
        value: "operation = 'sayHello'"
      endpoints:
        - "someEndpoint"
        - "anotherEndpoint"
Spring XML
<purge-endpoints>
  <selector>
    <value>operation = 'sayHello'</value>
  </selector>
  <endpoint name="someEndpointName"/>
  <endpoint name="anotherEndpointName"/>
</purge-endpoints>

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.

Spring Bean
@Bean
public SequenceBeforeTest beforeTest() {
    return SequenceBeforeTest.Builder.beforeTest()
            .actions(
                purgeEndpoints()
                    .endpoint("fooChannel")
                    .endpoint("barChannel")
            );
}
Spring XML
<spring:beans xmlns:spring="http://www.springframework.org/schema/beans"
              xmlns:citrus="http://www.citrusframework.org/schema/config">
    <citrus:before-test id="purgeBeforeTest">
        <citrus:actions>
            <purge-endpoints>
                <channel name="fooChannel"/>
                <channel name="barChannel"/>
            </purge-endpoints>
        </citrus:actions>
    </citrus:before-test>
</spring:beans>

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 too 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.

8.21. 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 simply assert a Java exception to be thrown during execution. See the example for an assert action definition in a test case:

Java
@CitrusTest
public void assertFailureTest() {
    $(assertException()
            .exception(org.citrusframework.exceptions.CitrusRuntimeException.class)
            .message("Unknown variable ${date}")
            .when(
                echo().message("Current date is: ${date}")
            )
    );
}
XML
<test name="AssertFailureTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <assert exception="org.citrusframework.exceptions.CitrusRuntimeException"
                message="Unknown variable ${date}">
            <when>
                <echo>
                    <message>Current date is: ${date}</message>
                </echo>
            </when>
        </assert>
    </actions>
</test>
YAML
name: "AssertFailureTest"
actions:
  - assert:
      exception: "org.citrusframework.exceptions.CitrusRuntimeException"
      message: 'Unknown variable ${date}'
      when:
        - echo:
            message: 'Current date is: ${date}'
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="AssertFailureTest">
        <actions>
            <assert exception="org.citrusframework.exceptions.CitrusRuntimeException"
                    message="Unknown variable ${date}">
                <when>
                    <echo>
                        <message>Current date is: ${date}</message>
                    </echo>
                </when>
            </assert>
        </actions>
    </testcase>
</spring:beans>
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 expect the failure to happen the test continues with its work once the assertion is done successfully.

8.22. 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.

Java
@CitrusTest
public void catchFailureTest() {
    $(catchException()
        .exception(CitrusRuntimeException.class)
        .when(
            echo().message("Current date is: ${date}")
        );
    );
}
XML
<test name="CatchFailureTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <catch exception="org.citrusframework.exceptions.CitrusRuntimeException">
            <when>
                <echo>
                    <message>Current date is: ${date}</message>
                </echo>
            </when>
        </assert>
    </actions>
</test>
YAML
name: "CatchFailureTest"
actions:
  - catch:
      exception: "org.citrusframework.exceptions.CitrusRuntimeException"
      when:
        - echo:
            message: 'Current date is: ${date}'
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="catchExceptionTest">
        <actions>
            <catch exception="org.citrusframework.exceptions.CitrusRuntimeException">
                <echo>
                    <message>Current date is: ${date}</message>
                </echo>
            </catch>
        </actions>
    </testcase>
</spring:beans>
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 no 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.

8.23. 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:

Java
@CitrusTest
public void antRunTest() {
    variable("today", "citrus:currentDate()");

    $(antrun()
        .buildFilePath("classpath:org/citrusframework/actions/build.xml")
        .target("sayHello")
        .property("date", "${today}")
        .property("welcomeText", "$Hello!"))
    );
}
XML
<test name="AntRunTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <variables>
        <variable name="today" value="citrus:currentDate()"/>
    </variables>
    <actions>
        <ant build-file="classpath:org/citrusframework/actions/build.xml">
            <execute target="sayHello"/>
            <properties>
                <property name="date" value="${today}"/>
                <property name="welcomeText" value="Hello!"/>
            </properties>
        </ant>
    </actions>
</test>
YAML
name: AntRunTest
variables:
  - name: "today"
    value: "citrus:currentDate()"
actions:
  - ant:
      build-file: "classpath:org/citrusframework/actions/build.xml"
      execute:
        target: "sayHello"
      properties:
        - name: "date"
          value: "${today}"
        - name: "welcomeText"
          value: "Hello!"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="AntRunTest">
        <variables>
            <variable name="today" value="citrus:currentDate()"/>
        </variables>
        <actions>
            <ant build-file="classpath:org/citrusframework/actions/build.xml">
                <execute target="sayHello"/>
                <properties>
                    <property name="date" value="${today}"/>
                    <property name="welcomeText" value="Hello!"/>
                </properties>
            </ant>
        </actions>
    </testcase>
</spring:beans>

The respective build.xml Ant file must provide the target to call. For example:

build.xml
<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:

Java
@CitrusTest
public void antRunTest() {
    variable("today", "citrus:currentDate()");

    $(antrun()
        .buildFilePath("classpath:org/citrusframework/actions/build.xml")
        .targets("sayHello", "sayGoodbye")
        .property("date", "${today}"))
    );
}
XML
<test name="AntRunTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <variables>
        <variable name="today" value="citrus:currentDate()"/>
    </variables>
    <actions>
        <ant build-file="classpath:org/citrusframework/actions/build.xml">
            <execute targets="sayHello,sayGoodbye"/>
            <properties>
                <property name="date" value="${today}"/>
            </properties>
        </ant>
    </actions>
</test>
YAML
name: AntRunTest
variables:
  - name: "today"
    value: "citrus:currentDate()"
actions:
  - ant:
      build-file: "classpath:org/citrusframework/actions/build.xml"
      execute:
        targets: "sayHello,sayGoodbye"
      properties:
        - name: "date"
          value: "${today}"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="AntRunTest">
        <variables>
            <variable name="today" value="citrus:currentDate()"/>
        </variables>
        <actions>
            <ant build-file="classpath:org/citrusframework/actions/build.xml">
                <execute targets="sayHello,sayGoodbye"/>
                <properties>
                    <property name="date" value="${today}"/>
                </properties>
            </ant>
        </actions>
    </testcase>
</spring:beans>

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.

Java
@Autowired
private BuildListener customBuildListener;

@CitrusTest
public void antRunTest() {
    $(antrun()
        .buildFilePath("classpath:org/citrusframework/actions/build.xml")
        .target("sayHello")
        .propertyFile("classpath:org/citrusframework/actions/build.properties")
        .listener(customBuildListener))
    );
}
XML
<test name="AntRunTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <ant build-file="classpath:org/citrusframework/actions/build.xml"
             build-listener="customBuildListener">
            <execute target="sayHello"/>
            <properties  file="classpath:org/citrusframework/actions/build.properties" />
        </ant>
    </actions>
</test>
YAML
name: AntRunTest
actions:
  - ant:
      build-file: "classpath:org/citrusframework/actions/build.xml"
      build-listener: "customBuildListener"
      execute:
        target: "sayHello"
      properties-file: "classpath:org/citrusframework/actions/build.properties"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="AntRunTest">
        <actions>
            <ant build-file="classpath:org/citrusframework/actions/build.xml"
                 build-listener="customBuildListener">
                <execute target="sayHello"/>
                <properties file="classpath:org/citrusframework/actions/build.properties"/>
            </ant>
        </actions>
    </testcase>
</spring:beans>

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.

8.24. 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:

Java
@Autowired
@Qualifier("myMailServer")
private MailServer myMailServer;

@CitrusTest
public void manageServerTest() {
    $(start()
        .server(myMailServer));

    $(sequential()
        .actions(...)); // do something

    $(stop()
        .server(myMailServer));
}
XML
<test name="ManageServerTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <start server="myMailServer"/>

        <sequential>
            <!-- do something -->
        </sequential>

        <stop server="myMailServer"/>
    </actions>
</test>
YAML
name: ManageServerTest
actions:
  - start:
      server: "myMailServer"
  - sequential:
      actions: {}
  - stop:
      server: "myMailServer"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="manageServerTest">
        <actions>
            <start server="myMailServer"/>

            <sequential>
                <!-- do something -->
            </sequential>

            <stop server="myMailServer"/>
        </actions>
    </testcase>
</spring:beans>

The start and stop server test action receive a server name which references a Spring bean component of type org.citrusframework.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.

Java
@Autowired
@Qualifier("myMailServer")
private MailServer myMailServer;

@Autowired
@Qualifier("myFtpServer")
private FtpServer myFtpServer;

@CitrusTest
public void manageServerTest() {
    $(start()
        .server(myMailServer, myFtpServer));

    $(sequential()
        .actions(...)); // do something

    $(stop()
        .server(myMailServer, myFtpServer));
}
XML
<test name="ManageServerTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <start>
            <servers>
                <server name="myMailServer"/>
                <server name="myFtpServer"/>
            </servers>
        </start>

        <sequential>
            <!-- do something -->
        </sequential>

        <stop>
            <servers>
                <server name="myMailServer"/>
                <server name="myFtpServer"/>
            </servers>
        </stop>
    </actions>
</test>
YAML
name: ManageServerTest
actions:
  - start:
      servers:
        - "myMailServer"
        - "myFtpServer"
  - sequential:
      actions: {}
  - stop:
      servers:
        - "myMailServer"
        - "myFtpServer"
Spring XML
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase"
              xmlns:spring="http://www.springframework.org/schema/beans">
    <testcase name="manageServerTest">
        <actions>
            <start>
                <servers>
                    <server name="myMailServer"/>
                    <server name="myFtpServer"/>
                </servers>
            </start>

            <sequential>
                <!-- do something -->
            </sequential>

            <stop>
                <servers>
                    <server name="myMailServer"/>
                    <server name="myFtpServer"/>
                </servers>
            </stop>
        </actions>
    </testcase>
</spring:beans>

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 care of 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.

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 org.citrusframework.server.Server. All custom server implementations can then be started and stopped during a test case.

8.25. 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:

Java
@CitrusTest
public void timerTest() {
    $(timer()
      .id("forkedTimer")
      .fork(true)
      .actions(sleep(50L))
    );

    $(timer()
      .fork(true)
      .actions(sleep(50L))
    );

    $(timer()
      .repeatCount(5)
      .actions(sleep(50L))
    );

    $(stopTimer("forkedTimer"));

    $(doFinally().actions(
      stopTimer()
    ));
}
XML
<test name="TimerTest" xmlns="http://citrusframework.org/schema/xml/testcase">
    <actions>
        <timer id="forkedTimer" fork="true">
            <actions>
                <sleep milliseconds="50" />
            </actions>
        </timer>

        <timer fork="true">
            <actions>
                <sleep milliseconds="50" />
            </actions>
        </timer>

        <timer repeatCount="5">
            <actions>
                <sleep milliseconds="50" />
            </actions>