Validation matcher
Message validation in Citrus is essential. The framework offers several validation mechanisms for different message types and formats. With test variables we are able to check for simple value equality. We ensure that message entries are equal to predefined expected values. Validation matcher add powerful assertion functionality on top of that. You just can use the predefined validation matcher functionalities in order to perform more complex assertions like contains or isNumber in your validation statements.
The following sections describe the Citrus default validation matcher implementations that are ready for usage. The matcher implementations should cover the basic assertions on character sequences and numbers. Of course you can add custom validation matcher implementations in order to meet your very specific validation assertions, too.
First of all let us have a look at a validation matcher statement in action so we understand how to use them in a test case.
<message>
<payload>
<RequestMessage>
<MessageBody>
<Customer>
<Id>@greaterThan(0)@</Id>
<Name>@equalsIgnoreCase('foo')@</Name>
</Customer>
</MessageBody>
</RequestMessage>
</payload>
</message>
The listing above describes a normal message validation block inside a receive test action. We use some inline message payload template as CDATA. As you know Citrus will compare the actual message payload to this expected template in DOM tree comparison. In addition to that you can simply include validation matcher statements. The message element Id is automatically validated to be a number greater than zero and the Name character sequence is supposed to match 'foo' ignoring case spelling considerations.
Please note the special validation matcher syntax. The statements are surrounded with '@' markers and are identified by some unique name. The optional parameters passed to the matcher implementation state the expected values to match.
Tip You can use validation matcher with all validation mechanisms - not only with XML validation. Plaintext, JSON, SQL result set validation are also supported.
A set of validation matcher implementations is usually combined to a validation matcher library. The library has a prefix that will identify the validation matcher inside the test case. The default test framework validation matcher library uses a default prefix (citrus). You can write your own validation matcher library using your own prefix in order to extend the test framework functionality whenever you want.
The library is built in the Spring configuration and contains a set of validation matcher that are of public use.
<citrus:validation matcher-library id="testMatcherLibrary" prefix="foo:">
<citrus:matcher name="isNumber"> class="com.consol.citrus.validation.matcher.core.IsNumberValidationMatcher"/>
<citrus:matcher name="contains"> class="com.consol.citrus.validation.matcher.core.ContainsValidationMatcher"/>
<citrus:matcher name="customMatcher"> ref="customMatcherBean"/>
...
</citrus:validation matcher-library>
As you can see the library defines one to many validation matcher members either referenced as normal Spring bean or by its implementing Java class name. Citrus constructs the library and you are able to use the validation matcher in your test case with the leading library prefix just like this:
@foo:isNumber()@
@foo:contains()@
@foo:customMatcher()@
Tip You can add custom validation matcher implementations and custom validation matcher libraries. Just use a custom prefix for your library. The default Citrus validation matcher library uses no prefix.See now the following sections describing the default validation validation matcher in Citrus.
matchesXml()
The XML validation matcher implementation is the possibly most exciting one, as we can validate nested XML with full validation power (e.g. ignoring elements, variable support). The matcher checks a nested XML fragment to compare against expected XML. For instance we receive following XML message payload for validation:
<GetCustomerMessage>
<CustomerDetails>
<Id>5</Id>
<Name>Christoph</Name>
<Configuration><![CDATA[
<config>
<premium>true</premium>
<last-login>2012-02-24T23:34:23</last-login>
<link>http://www.citrusframework.org/customer/5</link>
</config>
]]></Configuration>
</CustomerDetails>
</GetCustomerMessage>
As you can see the message payload contains some configuration as nested XML data in a CDATA section. We could validate this CDATA section as static character sequence comparison, true. But the
<message>
<payload>
<GetCustomerMessage>
<CustomerDetails>
<Id>5</Id>
<Name>Christoph</Name>
<Configuration>citrus:cdataSection('@matchesXml('<config>
<premium>${isPremium}</premium>
<last-login>@ignore@</last-login>
<link>http://www.citrusframework.org/customer/5</link>
</config>')@')</Configuration>
</CustomerDetails>
</GetCustomerMessage>
</payload>
</message>
With the validation matcher you are able to validate the nested XML with full validation power. Ignoring elements is possible and we can also use variables in our control XML.
Note Nested CDATA elements within other CDATA sections are not allowed by XML standard. This is why we create the nested CDATA section on the fly with the function cdataSection().### equalsIgnoreCase()
This matcher implementation checks for equality without any case spelling considerations. The matcher expects a single parameter as the expected character sequence to check for.
<value>@equalsIgnoreCase('foo')@</value>
contains()
This matcher searches for a character sequence inside the actual value. If the character sequence is not found somewhere the matcher starts complaining.
<value>@contains('foo')@</value>
The validation matcher also exist in a case insensitive variant.
<value>@containsIgnoreCase('foo')@</value>
startsWith()
The matcher implementation asserts that the given value starts with a character sequence otherwise the matcher will arise some error.
<value>@startsWith('foo')@</value>
endsWith()
Ends with matcher validates a value to end with a given character sequence.
<value>@endsWith('foo')@</value>
matches()
You can check a value to meet a regular expression with this validation matcher. This is for instance very useful for email address validation.
<value>@matches('[a-z0-9]')@</value>
matchesDatePattern()
Date values are always difficult to check for equality. Especially when you have millisecond timestamps to deal with. Therefore the date pattern validation matcher should have some improvement for you. You simply validate the date format pattern instead of checking for total equality.
<value>@matchesDatePattern('yyyy-MM-dd')@</value>
The example listing uses a date format pattern that is expected. The actual date value is parsed according to this pattern and may cause errors in case the value is no valid date matching the desired format.
isNumber()
Checking on values to be of numeric nature is essential. The actual value must be a numeric number otherwise the matcher raises errors. The matcher implementation does not evaluate any parameters.
<value>@isNumber()@</value>
lowerThan()
This matcher checks a number to be lower than a given threshold value.
<value>@lowerThan(5)@</value>
greaterThan()
The matcher implementation will check on numeric values to be greater than a minimum value.
<value>@greaterThan(5)@</value>
isWeekday()
The matcher works on date values and checks that a given date evaluates to the expected day of the week. The user defines the expected day by its name in uppercase characters. The matcher fails in case the given date is another week day than expected.
<someDate>@isWeekday('MONDAY')@</someDate>
Possible values for the expected day of the week are: MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY or SUNDAY.
The field value has to be a date value otherwise the matcher will fail to parse the date. The matcher requires a date format which is dd.MM.yyyy by default. You can change this date format as follows:
<someDate>@isWeekday(MONDAY('yyyy-MM-dd'))@</someDate>
Now the matcher uses the custom date format in order to parse the date value for evaluation. The validation matcher also works with date time values. In this case you have to give a valid date time format respectively (e.g. FRIDAY('yyyy-MM-dd'T'hh:mm:ss')).
variable()
This is a very special validation matcher. Instead of performing a validation logic you can save the actual value passed to the validation matcher as new test variable. This comes very handy as you can use the matcher wherever you want: JSON message payloads, XML message payloads, headers and so on.
<value>@variable('foo')@</value>
The validation matcher creates a new variable foo with the actual element value as variable value. When leaving out the control value the field name itself is used as variable name.
<date>@variable()@</date>
This creates a new variable date with the actual element value as variable value.
dateRange()
The matcher works on date values and checks that a given date is within the expected date range. The user defines the expected date range by specifying a from-date, a to-date and optionally a date format. The matcher fails when the given date lies outside the expected date range.
<someDate>@dateRange('01-12-2015', '31-12-2015', 'dd-MM-yyyy')@</someDate>
Possible valid values would be 'some date' >= '01-12-2015' and 'some date' <= '31-12-2015'
The date-format is optional and when omitted it is assumed that all dates match the default date format yyyy-MM-dd . When specifying a custom date format use java's date format as a reference for valid date formats. Only dates were used in the example above but we could just as easily used date and time as shown in the example below
<someDate>@dateRange('2015.12.01 07:00:00', '2015.12.01 19:00:00', 'yyyy.MM.dd HH:mm:ss')@</someDate>
assertThat()
Hamcrest is a very powerful matcher library with extraordinary matcher implementations. You can use Hamcrest matchers also as Citrus validation matcher.
<someValue>@assertThat(equalTo(foo))@</someValue>
In the listing above we are using the equalTo() matcher. All Hamcrest matchers are surrounded by a assertThat expression. You are able to combine several Hamcrest matchers then in order to construct very powerful validation logic. See the following examples on what is possible then:
<someValue>@assertThat(equalTo(value))@</someValue>
<someValue>@assertThat(not(equalTo(other))@</someValue>
<someValue>@assertThat(is(not(other))@</someValue>
<someValue>@assertThat(not(is(other))@</someValue>
<someValue>@assertThat(equalToIgnoringCase(VALUE)@</someValue>
<someValue>@assertThat(containsString(lue)@</someValue>
<someValue>@assertThat(not(containsString(other))@</someValue>
<someValue>@assertThat(startsWith(val)@</someValue>
<someValue>@assertThat(endsWith(lue)@</someValue>
<someValue>@assertThat(anyOf(startsWith(val), endsWith(lue))@</someValue>
<someValue>@assertThat(allOf(startsWith(val), endsWith(lue))@</someValue>
<someValue>@assertThat(isEmptyString()@</someValue>
<someValue>@assertThat(not(isEmptyString())@</someValue>
<someValue>@assertThat(isEmptyOrNullString()@</someValue>
<someValue>@assertThat(nullValue()@</someValue>
<someValue>@assertThat(notNullValue()@</someValue>
<someValue>@assertThat(empty()@</someValue>
<someValue>@assertThat(not(empty())@</someValue>
<someValue>@assertThat(greaterThan(4)@</someValue>
<someValue>@assertThat(allOf(greaterThan(4), lessThan(6), not(lessThan(5)))@</someValue>
<someValue>@assertThat(is(not(greaterThan(5)))@</someValue>
<someValue>@assertThat(greaterThanOrEqualTo(5)@</someValue>
<someValue>@assertThat(lessThan(5)@</someValue>
<someValue>@assertThat(not(lessThan(1))@</someValue>
<someValue>@assertThat(lessThanOrEqualTo(4)@</someValue>
<someValue>@assertThat(hasSize(5))@</someValue>
Citrus will automatically perform validation matchers on the element value. Only if all matchers are satisfied the validation will pass.