Service handlers
The architectural approach followed by GITB TDL is to capture in the test case the high level testing flow and delegate detailed domain-specific processing to separate services. These services can cover messaging between actors, complex processing or content validation and implement APIs that are defined in the GITB specification. The components implementing these services are termed generally handlers and, depending on their purpose can be:
Messaging handlers implementing the GITB messaging service API.
Processing handlers implementing the GITB processing service API.
Validation handlers implementing the GITB validation service API.
Another important distinction for handlers is whether they are built-in within the test bed software or external. For handlers that relate to domain-specific operations, the norm is to externalise them as remotely callable services. Nonetheless several common tasks that are frequently encountered in test cases are also available as built-in capabilities of the test engine.
In the sections that follow you can find:
The supported built-in handlers covering common tasks encountered in test cases.
The list of reusable external services maintained by the Test Bed (also usable locally as components).
Guidelines to implement custom external services to cover project-specific needs.
Specifying the handler implementation
Handlers are defined in the following steps:
btxn: When beginning a messaging transaction.
send: When sending a message outside of a messaging transaction.
receive: When receiving a message outside of a messaging transaction.
listen: When proxying a call to another service outside of a messaging transaction.
bptxn: When beginning a processing transaction.
process: When making a processing operation outside of a processing transaction.
verify: When validating content.
The element corresponding to each of these steps defines a handler
attribute to identify the handler implementation.
In case an built-in handler is to be used the value specified here is the name of the handler (see Built-in handlers). Using an external
handler implementation is achieved by specifying as the handler
value the address where the service’s WSDL file is
located. The test bed will automatically detect in this case that the handler is external and will internally replace local method
invocations with web service calls.
The value provided for the handler
attribute can also be provided with a pure variable reference (see Referring to variables)
allowing the actual value to be determined from configuration or even dynamically based on the test session context. In such a case the variable
reference is first evaluated to a string
that is then considered to determine whether the handler is a remote or built-in one.
The following example shows three validation steps taking place, the first one using the built-in XSDValidator, the second one using an external validation service, and the third one using an external validation service whose address is configurable:
<!--
Call a local, built-in validation handler called "XmlValidator"
-->
<verify handler="XmlValidator" desc="Validate content local">
<input name="xml">$docToValidate</input>
<input name="xsd">$schemaFile</input>
</verify>
<!--
Call a remote validation service handler
-->
<verify handler="https://serviceaddress?wsdl" desc="Validate content remote">
<input name="xml">$docToValidate</input>
<input name="schema">$schemaFile</input>
</verify>
<!--
Call a remote validation service handler (address in configuration)
-->
<verify handler="$DOMAIN{validationHandlerAddress}" desc="Validate content remote">
<input name="xml">$docToValidate</input>
<input name="schema">$schemaFile</input>
</verify>
Built-in handlers
The sections that follow list the handler implementations that already exist as predefined built-in implementations in the GITB test bed software.
Built-in messaging handlers
Note
Custom vs built-in messaging handlers: Built-in messaging handlers offer simple, out-of-the-box support for sending and receiving content to and from external systems. However, for complex messaging or testing needs you may be better suited by a custom implementation that would allow you to:
Use non-HTTP based protocols.
Manage security aspects including authentication and use of client certificates.
Produce customised step reports rather than a listing of request and response data.
Manage large payloads not supported directly by the test engine.
For receive steps, control fully exposed APIs and dynamically adapt responses based on received request data.
To start developing a custom messaging service you can use the published template and follow the step-by-step development guide.
The title of each section corresponds to the name of the handler that needs to be configured in the relevant step’s handler
attribute (in
send, receive or btxn steps).
HttpMessagingV2
Used to send or receive content over HTTP.
Use in send steps
To use this handler to send a HTTP message to a external system, set it as the handler
of a send step
(or the step’s transaction). In this case the supported step inputs are as follows:
Input name |
Required? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The URI of the endpoint to be called. |
|
No |
|
The HTTP method to use. Supported methods are GET (the default), POST, PUT, DELETE, HEAD, OPTIONS, PATCH and TRACE. |
|
No |
|
The HTTP headers to include, provided as a map of either strings (for single values) or lists of strings (for multiple values). |
|
No |
|
The HTTP body of the request. |
|
No |
|
The HTTP request parameters to include, provided as a map of strings (the map keys will be the parameter names). |
|
No |
|
The HTTP request parameters to include in the query string, provided as a map of strings (the map keys will be the parameter names). |
|
No |
|
The parts to include for a multipart request, provided either as a list of maps or a single map. Each map corresponds to a part and includes keys “name” (required), “content” (required), “fileName” and “contentType”. |
|
No |
|
Whether redirect responses should be followed (default is “true”). |
In terms of implicit default values for missing inputs and the processing logic applied, the test engine bases itself on the overall inputs provided. Specifically:
If no
method
is provided but abody
orparts
input is present, the request will be a POST.If
parameters
is used on a POST, PUT or PATCH these will be added as form-encoded parameters in the body, adding also the correct content type (“application/x-www-form-urlencoded”) as a HTTP header. If abody
is also provided then the parameters will be merged with anyqueryParameters
and added on the query string.If
parameters
is used on a GET, DELETE, HEAD, OPTIONS or TRACE they will be merged with anyqueryParameters
and added on the query string.If
parts
are provided for a multipart request, these are added as parts named using the relevant map’s “name” value, with content set to the map’s “content” value, and a content type set to the “contentType” value (defaulting to “application/octet-stream” if missing). If a “fileName” value is also provided, the part is added as a file part (as opposed to a text part if missing).
Once the send step completes, the resulting report will include two nested maps named request
and response
, corresponding
respectively to the submitted request and received response. The request
map includes the following:
Output name |
Always present? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The URI of the endpoint that was called. |
|
Yes |
|
The HTTP method used. |
|
No |
|
The HTTP headers explicitly added to the request, provided as a map of key (header name) to comma-separated string values (header values). |
|
No |
|
The HTTP body of the request that will be a map in case of a multipart request, with one entry per part. |
The response
map includes the following:
Output name |
Always present? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The HTTP status code of the response. |
|
No |
|
The HTTP headers of the response, provided as a map of key (header name) to comma-separated string values (header values). |
|
No |
|
The HTTP body of the response that will be a map in case of a multipart request, with one entry per part. |
The following GITB TDL snippets provide various examples using the HttpMessagingV2
handler in send steps, as well as accessing
outputs (see inline comments for details per case).
<!--
Make a GET call to https://my.sut.org/api/get.
-->
<send id="send1" desc="Call system" from="Actor1" to="Actor2" handler="HttpMessagingV2">
<input name="uri">"https://my.sut.org/api/get"</input>
</send>
<log>$send1{response}{status}</log> <!-- Print returned status. -->
<!--
Make a POST call to https://my.sut.org/api/post with form-encoded parameters in the request body.
-->
<assign to="send2Params{key1}">"value1"</assign>
<assign to="send2Params{key2}">"value 2"</assign> <!-- URL escaping is automatically handled. -->
<send id="send2" desc="Call system with parameters" from="Actor1" to="Actor2" handler="HttpMessagingV2">
<input name="uri">"https://my.sut.org/api/post"</input>
<input name="method">"POST"</input>
<input name="parameters">$send2Params</input>
</send>
<log>$send2{response}{body}</log> <!-- Print returned body. -->
<!--
Make a PUT call to https://my.sut.org/api/put with a body, headers and query string parameters.
-->
<assign to="send3Params{key1}">"value1"</assign>
<assign to="send3Params{key2}">"value2"</assign>
<assign to="send3Headers{Content-Type}">"application/xml"</assign>
<send id="send3" desc="Call system" from="Actor1" to="Actor2" handler="HttpMessagingV2">
<input name="uri">"https://my.sut.org/api/put"</input>
<input name="method">"PUT"</input>
<input name="body">$send3Body</input>
<input name="headers">$send3Headers</input>
<input name="parameters">$send3Params</input>
</send>
<log>$send3{request}{body}</log> <!-- Print request body (added by the test session). -->
<log>$send3{response}{body}</log> <!-- Print response body (returned by the SUT). -->
<log>$send3{response}{headers}{Content-Type}</log> <!-- Print response content type. -->
<!--
Make a DELETE call with a query string parameter.
-->
<assign to="send4Params{id}">"123"</assign>
<send id="send4" desc="Call system" from="Actor1" to="Actor2" handler="HttpMessagingV2">
<input name="uri">"https://my.sut.org/api/delete"</input>
<input name="method">"DELETE"</input>
<input name="parameters">$send4Params</input>
</send>
<log>$send4{response}{status}</log> <!-- Print returned status. -->
<!--
Make a DELETE call with an identifier in the URI as a path variable.
-->
<assign to="send5Identifier">"123"</assign>
<send id="send5" desc="Call system" from="Actor1" to="Actor2" handler="HttpMessagingV2">
<input name="uri">"https://my.sut.org/api/delete/" || $send5Identifier || "/"</input>
<input name="method">"DELETE"</input>
</send>
<log>$send5{response}{status}</log> <!-- Print returned status. -->
<!--
Make a multipart POST submission to a variable URI with extra query string parameters.
-->
<assign to="send6Part1{name}">"file1"</assign> <!-- A file part. -->
<assign to="send6Part1{contentType}">"text/xml"</assign>
<assign to="send6Part1{fileName}">"file1.xml"</assign>
<assign to="send6Part1{content}">$send6Part1Content</assign>
<assign to="send6Part2{name}" type="string">"text1"</assign> <!-- A text part. -->
<assign to="send6Part2{contentType}" type="string">"text/plain"</assign>
<assign to="send6Part2{content}" type="string">"A simple text value"</assign>
<assign to="send6Parts" append="true">$send6Part1</assign> <!-- Put parts together. -->
<assign to="send6Parts" append="true">$send6Part2</assign>
<assign to="send6Identifier">"123"</assign> <!-- Path variable. -->
<assign to="send6Params{key}">"value"</assign> <!-- Query string parameter. -->
<send id="send6" desc="Call system" from="Actor1" to="Actor2" handler="HttpMessagingV2">
<input name="uri">"https://my.sut.org/api/multi/" || $send6Identifier || "/"</input>
<input name="method">"POST"</input>
<input name="parts">$send6Parts</input>
<input name="parameters">$send6Params</input>
</send>
<log>$send6{response}{status}</log> <!-- Print returned status. -->
Use in receive steps
To use this handler to receive a HTTP message from an external system, set it as the handler
of a receive step
(or the step’s transaction). In this case the supported step inputs are as follows:
Input name |
Required? |
Type |
Description |
---|---|---|---|
|
No |
|
A URI extension following the base endpoint path at which the request will be expected. |
|
No |
|
The HTTP method to expect. Supported methods are GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH, TRACE. |
|
No |
|
The HTTP status to respond with (default is 200). |
|
No |
|
The HTTP headers to include in the response. |
|
No |
|
The HTTP body of the response. |
When the receive step executes, the test session will be suspended until a matching request is received from the system under test. The listening endpoint where this request will be received is constructed by concatenating in sequence:
The test engine’s base messaging URL.
The unique API key of the system under test.
If defined, the URI extension provided as the step’s
uriExtension
input.
As an example, for a Test Bed installation running locally with default settings, a system with API key “MY_API_KEY”, and a provided URI extension of “/test”; the listen endpoint will be:
http://localhost:8080/itbsrv/api/http/MY_API_KEY/test
Furthermore, if a method
has been provided as an input, a received request will need to have the same method to complete the step. If not provided,
any received request will complete the step. Finally, received query string parameters are ignored when matching incoming requests, except if a query string
has been included in the uriExtension
input. In any case, once the receive step executes, the listen endpoint and expected
method(s) will be added in the test session log.
...
[2024-06-14 16:31:35] INFO - Waiting to receive POST at [http://localhost:8080/itbsrv/api/http/7039EC8EX4786X4103XB40FXC7242D11E9B0/search] for step [Receive message 1]
Note
Supporting parallel test sessions: The listen endpoint for a receive step determines also whether test sessions can execute in parallel. If no uriExtension
is
provided, all test sessions for the system (at least the ones with receive steps) will need to execute sequentially. You can
configure the relevant test cases with supportsParallelExecution
as “false” to enforce this.
If possible, try to have uriExtension
differ across test cases, and ideally vary per test session.
Once the receive step completes, the resulting report will include two nested maps named request
and response
, corresponding
respectively to the received request and provided response. The request
map includes the following:
Output name |
Always present? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The URI of the endpoint that was called. |
|
Yes |
|
The HTTP method used. |
|
No |
|
The HTTP request headers, provided as a map of key (header name) to comma-separated string values (header values). |
|
No |
|
The HTTP body of the request that will be a map in case of a multipart request, with one entry per part. |
The response
map includes the following:
Output name |
Always present? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The HTTP status code of the response. |
|
No |
|
The HTTP headers of the response, provided as a map of key (header name) to comma-separated string values (header values). |
|
No |
|
The HTTP body of the response that will be a map in case of a multipart request, with one entry per part. |
The following GITB TDL snippets provide various examples using the HttpMessagingV2
handler in receive steps, as well as accessing
outputs (see inline comments for details per case).
<!--
Receive a GET call and respond with a 200 status.
The step will complete for a received GET to http://localhost:8080/itbsrv/api/http/MY_API_KEY/.
-->
<receive id="receive1" desc="Receive call" from="Actor1" to="Actor2" handler="HttpMessagingV2">
<input name="method">"GET"</input>
<input name="status">"200"</input>
</receive>
<log>$receive1{request}{uri}</log> <!-- Print the full URI of the request. -->
<!--
Receive a POST call at a dynamically generated URI extension and respond with a 200 status and JSON body.
The step will complete for a received POST to http://localhost:8080/itbsrv/api/http/MY_API_KEY/update/123/all.
-->
<assign to="receive2Identifier">"123"<assign>
<assign to="receive2Headers{Content-Type}">"application/json"<assign>
<receive id="receive2" desc="Receive call" from="Actor1" to="Actor2" handler="HttpMessagingV2">
<input name="method">"POST"</input>
<input name="uriExtension">"/update/" || $receive1Identifier || "/all"</input>
<input name="status">"200"</input>
<input name="headers">$receive1Headers</input>
<input name="body">$receive2Body</input>
</receive>
<log>$receive2{request}{body}</log> <!-- Print the request body. -->
<log>$receive2{request}{headers}{Content-Type}</log> <!-- Print the request content type header. -->
<log>$receive2{response}{body}</log> <!-- Print the response body. -->
<!--
Receive a multipart PUT call and respond with a 500 status and text error message.
The step will complete for a received PUT to http://localhost:8080/itbsrv/api/http/MY_API_KEY/multi.
-->
<assign to="receive3Headers{Content-Type}">"text/plain"<assign>
<receive id="receive3" desc="Receive call" from="Actor1" to="Actor2" handler="HttpMessagingV2">
<input name="method">"PUT"</input>
<input name="uriExtension">"/multi"</input>
<input name="status">"500"</input>
<input name="headers">$receive3Headers</input>
<input name="body">"Unsupported operation"</input>
</receive>
<log>$receive3{request}{uri}</log> <!-- Print the request URI. -->
<log>$receive3{request}{body}</log> <!-- Print the request body. -->
SimulatedMessaging
Used to add simulated messaging steps to the test execution diagram without any actual message exchanges taking place.
Input name |
Input type |
Required? |
Type |
Description |
---|---|---|---|---|
|
Send/receive input |
No |
|
An optional |
|
Send/receive input |
No |
|
An optional |
|
Send/receive input |
No |
|
Set to |
|
Receive input |
No |
|
An optional number of milliseconds to delay before presenting the receive step as completed. |
The following example illustrates usage of the SimulatedMessaging
handler to present a simulated exchange between actors, each
with its own report:
<assign to="map1{valueFile}">$templateFile</assign>
<assign to="map1{valueText}">'A text'</assign>
<send id="dataSend" desc="Send data" from="Actor1" to="Actor2" handler="SimulatedMessaging">
<input name="parameters">$map1</input>
</send>
<assign to="map2{valueFile}">$templateFile</assign>
<assign to="map2{valueText}">'Another text'</assign>
<receive id="dataReceive" desc="Receive data" from="Actor2" to="Actor1" reply="true" handler="SimulatedMessaging">
<input name="parameters">$map2</input>
<input name="result">'FAILURE'</input>
<input name="delay">3000</input>
</receive>
In case the SimulatedMessaging
handler displays reports with large content or complete files, we can also provide a hint to the test engine
on how such data is to be displayed. This is done by means of the contentTypes
input, an optional map
that can be set with
the content types (e.g. application/json
) to consider per parameter. When a content type is set for a given parameter this will
affect its syntax highlighting when displaying it within editors and also the type of file generated when it is downloaded.
The approach used to specify content types is to match fully, in terms of parameter names and structure, the corresponding
parameters
map. Matching of specific parameters is done on the basis of their map key and nesting level, whereas the
content type values are of type string
.
To clarify this, consider the following example where a SimulatedMessaging
handler is used for a send step,
displaying a report with two files (named input
and output
). Notice how the contentTypes
input is defined in a manner identical
to the actual data to be displayed.
<!-- Define the data. -->
<assign to="params{input}">$file1</assign>
<assign to="params{output}">$file2</assign>
<!-- Define the content types. -->
<assign to="contentTypes{input}">'application/json'</assign>
<assign to="contentTypes{output}">'application/xml'</assign>
<send desc="Send message" from="Actor1" to="Actor2" handler="SimulatedMessaging">
<input name="parameters">$params</input>
<input name="contentTypes">$contentTypes</input>
</send>
Content types don’t need to cover all parameters, only those for which they are relevant or known. For example in the following case we only define a content type for the first displayed file, omitting it for simple strings and for the second file for which the content type is unknown.
<!-- Define the data. -->
<assign to="params{aFile}">$file</assign>
<assign to="params{countryCode}">$countryCode</assign>
<assign to="params{message}">"Transformation was successful."</assign>
<assign to="params{aSecondFile}">$secondFile</assign>
<!-- Define the content type only for the first file. -->
<assign to="contentTypes{aFile}">'application/xml'</assign>
<send desc="Send message" from="Actor1" to="Actor2" handler="SimulatedMessaging">
<input name="parameters">$params</input>
<input name="contentTypes">$contentTypes</input>
</send>
Finally, the following example illustrates how content types can be provided when the parameters are defined within complex structures (maps and lists, nested at different levels).
<!-- Define the data. -->
<assign to="params{input}{file1}">$file1</assign>
<assign to="params{input}{file2}">$file2</assign>
<assign to="params{input}{messageId}">$messageIdentifier</assign>
<assign to="params{input}{attachments}" append="true">$attachment1</assign>
<assign to="params{input}{attachments}" append="true">$attachment2</assign>
<assign to="params{output}{response}">$response</assign>
<assign to="params{output}{message}">"Input processed successfully."</assign>
<!-- Define the content types. -->
<assign to="types{input}{file1}">"application/xml"</assign>
<assign to="types{input}{file2}">"application/xml"</assign>
<assign to="types{input}{attachments}" append="true">"text/plain"</assign>
<assign to="types{input}{attachments}" append="true">"application/pdf"</assign>
<assign to="types{output}{response}">"application/json"</assign>
<!-- Call the send step. -->
<send desc="Send message" from="Actor1" to="Actor2" handler="SimulatedMessaging">
<input name="parameters">$params</input>
<input name="contentTypes">$types</input>
</send>
SoapMessagingV2
Used to send or receive payloads via SOAP web service calls.
Use in send steps
To use this handler to call a SOAP web service of an external system, set it as the handler
of a send step
(or the step’s transaction). In this case the supported step inputs are as follows:
Input name |
Required? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The URI of the SOAP endpoint to be called. |
|
Yes |
|
The SOAP envelope to send (as an XML object). |
|
No |
|
The SOAP protocol version (either “1.1” or “1.2”). If not provided this is calculated from the provided Content-Type HTTP header, otherwise “1.1” is used. |
|
No |
|
The SOAPAction header to set. This overrides the value set in the HTTP headers (if present). |
|
No |
|
The MTOM attachments to set, provided as a list of maps or a single map. Each map corresponds to an attachment and includes keys “name” (required), “content” (required), “contentType” and “forceText” (a boolean to force the attachment’s inclusion as text). |
|
No |
|
Whether a non-SOAP response will be tolerated (otherwise the step results in a failure). |
Once the send step completes, the resulting report will include two nested maps named request
and response
, corresponding
respectively to the submitted request and received response. The request
map includes the following:
Output name |
Always present? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The URI of the endpoint that was called. |
|
Yes |
|
The SOAP envelope that was sent. |
|
Yes |
|
The SOAP body extracted from the envelope. |
|
Yes |
|
The HTTP headers added to the request, provided as a map of key (header name) to comma-separated string values (header values). |
|
No |
|
The MTOM attachments sent with the envelope. The key of each entry is the attachment name, whereas the value is the content. |
The response
map includes the following:
Output name |
Always present? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The HTTP status code of the response. |
|
No |
|
The SOAP envelope of the response. |
|
No |
|
The SOAP body extracted from the response envelope. |
|
No |
|
The HTTP headers from the response, provided as a map of key (header name) to comma-separated string values (header values). |
|
No |
|
The MTOM attachments sent with the envelope. The key of each entry is the attachment name, whereas the value is the content. |
The following GITB TDL snippets provide various examples using the SoapMessagingV2
handler in send steps, as well as accessing
outputs (see inline comments for details per case).
<!--
Make a SOAP 1.1 call to https://my.sut.org/api/service.
-->
<send id="send1" desc="Send SOAP message" from="Actor1" to="Actor2" handler="SoapMessagingV2">
<input name="uri">"https://my.sut.org/api/service"</input>
<input name="envelope">$send1Envelope</input>
</send>
<log>$send1{request}{envelope}</log> <!-- Print the SOAP envelope that was sent. -->
<log>$send1{response}{body}</log> <!-- Print the response's SOAP body. -->
<log>$send1{response}{headers}{Content-Type}</log> <!-- Print the response HTTP Content-Type header. -->
<!--
Make a SOAP 1.2 call to https://my.sut.org/api/service2 setting the SOAP action.
-->
<send id="send2" desc="Send SOAP message" from="Actor1" to="Actor2" handler="SoapMessagingV2">
<input name="uri">"https://my.sut.org/api/service2"</input>
<input name="envelope">$send2Envelope</input>
<input name="action">"http://my.sut.org/MyService/Operation1"</input>
<input name="version">"1.2"</input>
</send>
<log>$send2{request}{envelope}</log> <!-- Print the SOAP envelope that was sent. -->
<log>$send2{response}{body}</log> <!-- Print the response's SOAP body. -->
<log>$send2{response}{headers}{Content-Type}</log> <!-- Print the response HTTP Content-Type header. -->
<!--
Make a SOAP 1.1 call to https://my.sut.org/api/service3 with MTOM attachments (a binary file and a JSON document).
-->
<assign to="send3File1{name}">"id1"</assign>
<assign to="send3File1{content}">$send3Attachment1</assign>
<assign to="send3File1{contentType}">"application/zip"</assign>
<assign to="send3File2{name}">"id2"</assign>
<assign to="send3File2{content}">$send3Attachment2</assign>
<assign to="send3File2{contentType}">"application/json"</assign>
<assign to="send3File2{forceText}">true()</assign> <!-- Add the attachment inline as text rather than Base64. -->
<assign to="send3Attachments" append="true">$send3File1</assign>
<assign to="send3Attachments" append="true">$send3File2</assign>
<send id="send3" desc="Send SOAP message" from="Actor1" to="Actor2" handler="SoapMessagingV2">
<input name="uri">"https://my.sut.org/api/service3"</input>
<input name="envelope">$send3Envelope</input>
<input name="attachments">$send3Attachments</input>
</send>
<log>$send3{request}{envelope}</log> <!-- Print the SOAP envelope that was sent. -->
<log>$send3{request}{attachments}{id2}</log> <!-- Print the JSON file sent as an attachment. -->
<log>$send3{request}{headers}{Content-Type}</log> <!-- Print the HTTP Content-Type header that was generated for the request. -->
<log>$send3{response}{status}</log> <!-- Print the response status. -->
<log>$send3{response}{body}</log> <!-- Print the response SOAP body. -->
Use in receive steps
To use this handler to receive a SOAP web service call from an external system, set it as the handler
of a receive step
(or the step’s transaction). In this case the supported step inputs are as follows:
Input name |
Required? |
Type |
Description |
---|---|---|---|
|
No |
|
A URI extension following the base endpoint path at which the request will be expected. |
|
No |
|
The HTTP status code to return (default is 200). |
|
Yes |
|
The SOAP envelope to respond with (as an XML object). |
|
No |
|
The SOAP protocol version (either “1.1” or “1.2”) to construct the response with. If not provided this is calculated from the provided Content-Type HTTP header, otherwise “1.1” is used. |
|
No |
|
The MTOM attachments to include in the response, provided as a list of maps or a single map. Each map corresponds to an attachment and includes keys “name” (required), “content” (required), “contentType” and “forceText” (a boolean to force the attachment’s inclusion as text). |
|
No |
|
The HTTP headers to include in the response, provided as a map of key (header name) to comma-separated string values (header values). |
When the receive step executes, the test session will be suspended until a matching request is received from the system under test. The listening endpoint where this request will be received is constructed by concatenating in sequence:
The test engine’s base messaging URL.
The unique API key of the system under test.
If defined, the URI extension provided as the step’s
uriExtension
input.
As an example, for a Test Bed installation running locally with default settings, a system with API key “MY_API_KEY”, and a provided URI extension of “/test”; the listen endpoint will be:
http://localhost:8080/itbsrv/api/soap/MY_API_KEY/test
Received query string parameters are ignored when matching incoming requests, except if a query string has been included in the uriExtension
input.
In any case, once the receive step executes, the listen endpoint will be added in the test session log.
...
[2024-06-05 18:19:56] INFO - Waiting to receive SOAP message at [http://localhost:8080/itbsrv/api/soap/7039EC8EX4786X4103XB40FXC7242D11E9B0/service] for step [Receive SOAP message]
Note
Supporting parallel test sessions: The listen endpoint for a receive step determines also whether test sessions can execute in parallel. If no uriExtension
is
provided, all test sessions for the system (at least the ones with receive steps) will need to execute sequentially. You can
configure the relevant test cases with supportsParallelExecution
as “false” to enforce this.
If possible, try to have uriExtension
differ across test cases, and ideally vary per test session.
Once the receive step completes, the resulting report will include two nested maps named request
and response
, corresponding
respectively to the received request and provided response. The request
map includes the following:
Output name |
Always present? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The URI of the endpoint that was called. |
|
Yes |
|
The SOAP envelope that was received. |
|
Yes |
|
The SOAP body extracted from the envelope. |
|
Yes |
|
The request HTTP headers, provided as a map of key (header name) to comma-separated string values (header values). |
|
No |
|
The MTOM attachments received with the envelope. The key of each entry is the attachment name, whereas the value is the content. |
The response
map includes the following:
Output name |
Always present? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The HTTP status code of the response. |
|
No |
|
The SOAP envelope provided as the response. |
|
No |
|
The SOAP body extracted from the response envelope. |
|
No |
|
The response HTTP headers, provided as a map of key (header name) to comma-separated string values (header values). |
|
No |
|
The MTOM attachments sent with the response envelope. The key of each entry is the attachment name, whereas the value is the content. |
The following GITB TDL snippets provide various examples using the SoapMessagingV2
handler in send steps, as well as accessing
outputs (see inline comments for details per case).
<!--
Receive a SOAP call and respond with a success and specific envelope.
The step will complete for a received SOAP call to http://localhost:8080/itbsrv/api/soap/MY_API_KEY/.
-->
<receive id="receive1" desc="Receive SOAP message" from="Actor1" to="Actor2" handler="SoapMessagingV2">
<input name="envelope">$receive1Envelope</input>
</receive>
<log>$receive1{request}{body}</log> <!-- Print the received SOAP body. -->
<log>$receive1{response}{envelope}</log> <!-- Print the SOAP envelope sent as the response. -->
<!--
Receive a SOAP call at a specific URI extension and respond with a 500 status and SOAP fault using SOAP version 1.2.
The step will complete for a received SOAP call to http://localhost:8080/itbsrv/api/soap/MY_API_KEY/service.
-->
<receive id="receive2" desc="Receive SOAP message" from="Actor1" to="Actor2" handler="SoapMessagingV2">
<input name="uriExtension">"/service"</input>
<input name="status">"500"</input>
<input name="version">"1.2"</input>
<input name="envelope">$receive2FaultEnvelope</input>
</receive>
<log>$receive2{request}{body}</log> <!-- Print the received SOAP body. -->
<log>$receive2{response}{status}</log> <!-- Print the status of the response. -->
<!--
Receive a SOAP call at a specific URI extension and respond with a success and envelope along with two MTOM attachments (a binary file and a JSON document).
The step will complete for a received SOAP call to http://localhost:8080/itbsrv/api/soap/MY_API_KEY/service.
-->
<assign to="receive3File1{name}">"id1"</assign>
<assign to="receive3File1{content}">$receive3Attachment1</assign>
<assign to="receive3File1{contentType}">"application/zip"</assign>
<assign to="receive3File2{name}">"id2"</assign>
<assign to="receive3File2{content}">$receive3Attachment2</assign>
<assign to="receive3File2{contentType}">"application/json"</assign>
<assign to="receive3File2{forceText}">true()</assign> <!-- Add the attachment inline as text rather than Base64. -->
<assign to="receive3Attachments" append="true">$receive3File1</assign>
<assign to="receive3Attachments" append="true">$receive3File2</assign>
<receive id="receive3" desc="Receive SOAP message" from="Actor1" to="Actor2" handler="SoapMessagingV2">
<input name="uriExtension">"/service"</input>
<input name="envelope">$receive3Envelope</input>
<input name="attachments">$receive3Attachments</input>
</receive>
Built-in processing handlers
The title of each section corresponds to the name of the handler that needs to be configured in the relevant step’s handler
attribute (in
process or bptxn steps).
Base64Processor
Used to manipulate Base64-encoded content for use in test cases. This processing handler supports but does not require a processing transaction to be established. The following operations are supported:
Operation |
Description |
Input(s) |
Output(s) |
---|---|---|---|
|
Receive a |
Yes |
A |
|
Receive a |
Yes |
A |
The input parameters expected by the different operations are as follows:
Operation |
Input name |
Required? |
Description |
---|---|---|---|
|
|
Yes |
The |
|
|
No |
A |
|
|
Yes |
The |
Base64 encoding is a technique often used to represent arbitrary byte sequences as text. Using this processing handler you can work with Base64 encoded texts
that need to be decoded in test cases, but also encode binary content where this is needed. In both the encoding and decoding steps there is support for Base64
content and also data URLs. Data URLs are commonly used in web representations for the inline definition of binary resources. A data URL is essentially the
Base64-encoded bytes prefixed with the content’s mime type as data:[mime type],base64,[BASE64 encoded string]
(e.g. data:application/xml;base64,YXNoZGl1cXcgaGRva...
).
The following examples illustrate use of this handler to work with Base64 encoding:
<!--
Encode the binary variable "aBinaryVariable" and return the encoded string as "data1{output}".
-->
<process id="data1" handler="Base64Processor">
<operation>encode</operation>
<input name="input">$aBinaryVariable</input>
</process>
<!--
Encode the binary variable "aBinaryVariable" and return the encoded string as "data2{output}".
The result in this case is formatted as a data URL.
-->
<process id="data2" handler="Base64Processor">
<operation>encode</operation>
<input name="input">$aBinaryVariable</input>
<input name="dataUrl">'true'</input>
</process>
<!--
Decode a Base-64 encoded string to return its binary equivalent as "data3{output}". In this case
the result will be identical to the "aBinaryVariable" variable used in the first step.
-->
<process id="data3" handler="Base64Processor">
<operation>decode</operation>
<input name="input">$data1{output}</input>
</process>
<!--
Decode a Base-64 encoded string to return its binary equivalent as "data4{output}". In this example
the handler is provided the data URL produced in the second step and will result in the same output
as "data3" that matches the original input ("aBinaryVariable").
-->
<process id="data4" handler="Base64Processor">
<operation>decode</operation>
<input name="input">$data2{output}</input>
</process>
CollectionUtils
Used to process collections (maps and lists) in ways not possible otherwise with TDL expressions. This processing handler does not require a processing transaction to be established. The following operations are supported:
Operation |
Description |
Input(s) |
Output(s) |
---|---|---|---|
|
Receive a collection as input and empty it. |
Yes |
No. |
|
Check to see whether a collection contains a given value. |
Yes |
Yes, a |
|
Return a random key from a map. |
Yes |
Yes, one of the map’s |
|
Return a random value from a collection. |
Yes |
Yes, the selected value (type varies depending on the content). |
|
Remove an entry from a collection. |
Yes |
No. |
|
Receive a collection as input and return the number of elements it contains. |
Yes |
Yes, a |
The input parameters expected by the different operations are as follows:
Operation |
Input name |
Required? |
Description |
---|---|---|---|
|
|
No |
The |
|
|
No |
The |
|
|
No |
The |
|
|
No |
The |
|
|
Yes |
The value to look for. |
|
|
No |
The |
|
|
No |
The |
|
|
No |
The |
|
|
No |
The |
|
|
No |
The |
|
|
Yes |
In case of a |
|
|
No |
The |
|
|
No |
The |
Collection or container variables represent flexible means of recording arbitrary sequences of data or hierarchical data structures. In particular
map
variables are very common as these are used to store results of processing, messaging and validation operations.
Adding new elements to collections or replacing existing values is achieved using the assign step, where the
expressions used may also determine collections that don’t previously exist.
The CollectionUtils
processing handler complements such operations by allowing further manipulations that cannot be achieved
through simple expressions.
The size
operation allows a test case to determine a collection’s size. This can be particularly useful in the case of
operations that return an arbitrary number of data items as a list
which we need to iterate over. The following examples
illustrate how this operation can be used:
<!-- Create a map with three elements -->
<assign to="aMap{a}">'Value 1'</assign>
<assign to="aMap{b}">'Value 2'</assign>
<assign to="aMap{c}">'Value 3'</assign>
<!-- Create a list with two elements -->
<assign to="aList" append="true">'Value 1'</assign>
<assign to="aList" append="true">'Value 2'</assign>
<!-- Calculate the size of the map -->
<process id="aMapSize" handler="CollectionUtils">
<operation>size</operation>
<input name="map">$aMap</input>
</process>
<!-- Prints "3" -->
<log>$aMapSize{output}</log>
<!-- Calculate the size of the list -->
<process id="aListSize" handler="CollectionUtils">
<operation>size</operation>
<input name="list">$aList</input>
</process>
<!-- Prints "2" -->
<log>$aListSize{output}</log>
<!-- Print each list element. -->
<foreach desc="Iterate list" counter="index" start="0" end="$aListSize{output}">
<do>
<!-- Prints "Value 1" and then "Value 2" -->
<log>aList{$index}</log>
</do>
</foreach>
Note
Nested collections: If a collection structure contains itself further collection structures as elements, the
size
operation will only count the collection’s top level elements.
The clear
operation on the other hand allows a test case to empty the contents of a given collection if this becomes
necessary. The following examples illustrate how this works for lists and maps:
<!-- Create a map with three elements -->
<assign to="aMap{a}">'Value 1'</assign>
<assign to="aMap{b}">'Value 2'</assign>
<assign to="aMap{c}">'Value 3'</assign>
<!-- Create a list with two elements -->
<assign to="aList" append="true">'Value 1'</assign>
<assign to="aList" append="true">'Value 2'</assign>
<!-- Empty the map -->
<process handler="CollectionUtils">
<operation>clear</operation>
<input name="map">$aMap</input>
</process>
<!-- Empty the list -->
<process handler="CollectionUtils">
<operation>clear</operation>
<input name="list">$aList</input>
</process>
The contains
operation allows for a simple lookup of a value with a collection. In case the collection is a map
, the lookup is
done on the basis of the entries’ keys. Otherwise for a list
the lookup considers the contained elements’ values. The following examples
illustration the operation’s use:
<!-- Create a map -->
<assign to="aMap{a}">'Value 1'</assign>
<assign to="aMap{b}">'Value 2'</assign>
<assign to="aMap{c}">'Value 3'</assign>
<!-- Lookup an existing value -->
<process handler="CollectionUtils" output="mapCheck1" operation="contains">
<input name="map">$aMap</input>
<input name="value">'b'</input>
</process>
<!-- Prints "true" -->
<log>$mapCheck1</log>
<!-- Lookup an non-existing value -->
<process handler="CollectionUtils" output="mapCheck2" operation="contains">
<input name="map">$aMap</input>
<input name="value">'x'</input>
</process>
<!-- Prints "false" -->
<log>$mapCheck2</log>
<!-- Create a list -->
<assign to="aList" append="true">'Value 1'</assign>
<assign to="aList" append="true">'Value 2'</assign>
<assign to="aList" append="true">'Value 3'</assign>
<!-- Lookup an existing value -->
<process handler="CollectionUtils" output="listCheck1" operation="contains">
<input name="map">$aList</input>
<input name="value">'Value 1'</input>
</process>
<!-- Prints "true" -->
<log>$listCheck1</log>
<!-- Lookup an non-existing value -->
<process handler="CollectionUtils" output="listCheck2" operation="contains">
<input name="map">$aList</input>
<input name="value">'Value X'</input>
</process>
<!-- Prints "false" -->
<log>$listCheck2</log>
Using the randomKey
and randomValue
operations we can retrieve random entries from collections. The following
examples illustrate their usage:
<!-- Create a map -->
<assign to="aMap{a}">'Value 1'</assign>
<assign to="aMap{b}">'Value 2'</assign>
<!-- Get one of the map's keys -->
<process handler="CollectionUtils" output="value1" operation="randomKey">
<input name="map">$aMap</input>
</process>
<!-- Prints either "a" or "b" -->
<log>$value1</log>
<!-- Get one of the map's values -->
<process handler="CollectionUtils" output="value2" operation="randomValue">
<input name="map">$aMap</input>
</process>
<!-- Prints either "Value 1" or "Value 2" -->
<log>$value2</log>
<!-- Create a list -->
<assign to="aList" append="true">'Value 1'</assign>
<assign to="aList" append="true">'Value 2'</assign>
<!-- Get one of the list's values -->
<process handler="CollectionUtils" output="value3" operation="randomValue">
<input name="list">$aList</input>
</process>
<!-- Prints either "Value 1" or "Value 2" -->
<log>$value3</log>
The remove
operation is used to remove specific entries from a collection. When using a map the removed entry is matched
based on its key. For lists, the entry to remove is identified by its zero-based index. The following examples illustrate the
operation’s use:
<!-- Create a map -->
<assign to="aMap{a}">'Value 1'</assign>
<assign to="aMap{b}">'Value 2'</assign>
<!-- Remove the entry with key "a" -->
<process handler="CollectionUtils" operation="remove">
<input name="map">$aMap</input>
<input name="item">'a'</input>
</process>
<!-- Create a list -->
<assign to="aList" append="true">'Value 1'</assign>
<assign to="aList" append="true">'Value 2'</assign>
<!-- Remove the second entry from the list -->
<process handler="CollectionUtils" operation="remove">
<input name="list">$aList</input>
<!-- Providing "1" given that indexes are zero-based -->
<input name="item">1</input>
</process>
DelayProcessor
Used to pause a test session for a given duration. The following operations are supported:
Operation |
Description |
Input(s) |
Output(s) |
---|---|---|---|
|
Delay the test session for a given duration. |
Yes |
No. |
The input parameters expected by the delay
operation are as follows:
Operation |
Input name |
Required? |
Description |
---|---|---|---|
|
|
Yes |
A |
The following examples illustrate how the DelayProcessor
can be used to force the test session to wait.
<!-- Wait for a fixed 5 seconds before proceeding. -->
<process handler="DelayProcessor" operation="delay" input="5000"/>
<!-- Wait for a random duration between 5 and 10 seconds. -->
<process handler="TokenGenerator" operation="random" output="randomDelay">
<input name="minimum">5</input>
<input name="maximum">10</input>
<input name="integer">true()</input>
</process>
<process handler="DelayProcessor" operation="delay" output="$randomDelay"/>
DisplayProcessor
Used to display arbitrary content to users as a report. Using this instead of a user interaction step allows you to display content when the user clicks the relevant step report, as opposed to always producing a popup. This makes it a useful mechanism for including additional information in the test’s output without distracting the user. The following operation is supported:
Operation |
Description |
Input(s) |
Output(s) |
---|---|---|---|
|
Include the provided data in a step report that the user may choose to view. |
Yes |
No. |
The input parameters expected by the display
operation are as follows:
Operation |
Input name |
Required? |
Description |
---|---|---|---|
|
|
No |
A |
|
|
No |
A |
|
|
No |
A |
The following example illustrates usage of the DisplayProcessor
to create a step report for a given set of data that the user may
choose to view:
<!-- Display a report based on a set of parameters. -->
<assign to="parameters{textValue}">'A sample value'</assign>
<assign to="parameters{listValues}" append="true">'Value 1'</assign>
<assign to="parameters{listValues}" append="true">'Value 2'</assign>
<assign to="parameters{listValues}" append="true">`Value 3`</assign>
<process desc="Show values" hidden="false" handler="DisplayProcessor" input="$parameters"/>
<!-- Display a report but also mark the step as failed if we have errors. -->
<assign to="result">if ($status = "OK") then "SUCCESS" else "FAILURE"</assign>
<assign to="report{comments}">$errorDescription</assign>
<process desc="Show values" hidden="false" handler="DisplayProcessor">
<input name="result">$result</input>
<input name="parameters">$report</input>
</process>
In case the DisplayProcessor
is used to display large content or complete files, we can also provide a hint to the test engine
on how such data is to be displayed. This is done by means of the contentTypes
input, an optional map
that can be set with
the content types (e.g. application/json
) to consider per parameter. When a content type is set for a given parameter this will
affect its syntax highlighting when displaying it within editors and also the type of file generated when it is downloaded.
The approach used to specify content types is to match fully, in terms of parameter names and structure, the corresponding
parameters
map. Matching of specific parameters is done on the basis of their map key and nesting level, whereas the
content type values are of type string
.
To clarify this, consider the following example where a DisplayProcessor
is used to display two files (named input
and output
).
Notice how the contentTypes
input is defined in a manner identical to the actual data to be displayed.
<!-- Define the data. -->
<assign to="params{input}">$file1</assign>
<assign to="params{output}">$file2</assign>
<!-- Define the content types. -->
<assign to="contentTypes{input}">'application/json'</assign>
<assign to="contentTypes{output}">'application/xml'</assign>
<process desc="Process data" hidden="false" handler="DisplayProcessor">
<input name="parameters">$params</input>
<input name="contentTypes">$contentTypes</input>
</process>
Content types don’t need to cover all parameters, only those for which they are relevant or known. For example in the following case we only define a content type for the first displayed file, omitting it for simple strings and for the second file for which the content type is unknown.
<!-- Define the data. -->
<assign to="params{aFile}">$file</assign>
<assign to="params{countryCode}">$countryCode</assign>
<assign to="params{message}">"Transformation was successful."</assign>
<assign to="params{aSecondFile}">$secondFile</assign>
<!-- Define the content type only for the first file. -->
<assign to="contentTypes{aFile}">'application/xml'</assign>
<process desc="Process data" hidden="false" handler="DisplayProcessor">
<input name="parameters">$params</input>
<input name="contentTypes">$contentTypes</input>
</process>
Finally, the following example illustrates how content types can be provided when the parameters are defined within complex structures (maps and lists, nested at different levels).
<!-- Define the data. -->
<assign to="params{input}{file1}">$file1</assign>
<assign to="params{input}{file2}">$file2</assign>
<assign to="params{input}{messageId}">$messageIdentifier</assign>
<assign to="params{input}{attachments}" append="true">$attachment1</assign>
<assign to="params{input}{attachments}" append="true">$attachment2</assign>
<assign to="params{output}{response}">$response</assign>
<assign to="params{output}{message}">"Input processed successfully."</assign>
<!-- Define the content types. -->
<assign to="types{input}{file1}">"application/xml"</assign>
<assign to="types{input}{file2}">"application/xml"</assign>
<assign to="types{input}{attachments}" append="true">"text/plain"</assign>
<assign to="types{input}{attachments}" append="true">"application/pdf"</assign>
<assign to="types{output}{response}">"application/json"</assign>
<!-- Call the process step. -->
<process desc="Process data" hidden="false" handler="DisplayProcessor">
<input name="parameters">$params</input>
<input name="contentTypes">$types</input>
</process>
Note
DisplayProcessor in non-hidden steps: Process steps are by default set as hidden
, meaning
that they execute but are not displayed and do not produce a visible report. When using the DisplayProcessor
you need
to ensure that hidden
is set to false
for its use to be meaningful.
JSONPointerProcessor
Used to extract values or complete elements from JSON content based on a provided JSON Pointer expression. The following operations are supported:
Operation |
Description |
Input(s) |
Output(s) |
---|---|---|---|
|
Use a JSON Pointer expression to extract a value or full elements from the provided JSON content. |
Yes |
A |
The input parameters expected by the process
operation are as follows:
Operation |
Input name |
Required? |
Description |
---|---|---|---|
|
|
Yes |
A |
|
|
Yes |
A |
The following example illustrates how the JSONPointerProcessor
can be used to extract a value from JSON content provided by the user via
an interact step:
<!--
Have the user enter the JSON content to process.
-->
<interact id="data" desc="Receive input">
<request name="json" desc="Enter JSON content:" inputType="CODE" mimeType="application/json"/>
</interact>
<!--
Retrieve a value from the provided JSON content. The content is expected to structured as follows:
{
"user": {
"address": {
"streetName": "An address"
}
}
}
-->
<process handler="JSONPointerProcessor" operation="process" output="result">
<input name="content">$data{json}</input>
<input name="pointer">"/user/address/streetName"</input>
</process>
<!--
Log the result.
-->
<log>$result</log>
RegExpProcessor
Used to process texts using regular expressions, to verify whether they match a specific pattern or to extract data. This processing handler does not require a processing transaction to be established. The following operations are supported:
Operation |
Description |
Input(s) |
Output(s) |
---|---|---|---|
|
Check to see if a |
Yes |
Yes, a |
|
Use an expression to collect data from a provided |
Yes |
A |
The input parameters expected by the different operations are as follows:
Operation |
Input name |
Required? |
Description |
---|---|---|---|
|
|
Yes |
The |
|
|
Yes |
A |
|
|
Yes |
The |
|
|
Yes |
A |
Regular expressions offer a very powerful means of describing a text’s content and extracting from it certain parts for further processing. They can be used against any text content, offering a counterpart to the use of XPath in the assign step that is best adapted, but also limited, to XML structures. The regular expressions are expected to be provided using the syntax used by the Java language.
The check
operation can be used to verify whether a given text matches a specific pattern. This may at first appear similar to the
RegExpValidator, however there is a subtle difference: using the RegExpValidator
constitutes an assertion made by the test case which, if failed, would likely mean that the test session itself is considered failed. The check
operation doesn’t presume anything for the test session’s status, but is rather used as an internal check to e.g. determine
whether an optional set of steps should be followed. The following example illustrates its use:
<!-- Check if a given text includes "test" in a case-insensitive manner -->
<process id="check" handler="RegExpProcessor">
<operation>check</operation>
<input name="input">$someTextData</input>
<!-- Flags are passed in embedded format (e.g. case insensitive match). -->
<input name="expression">"(?i)test"</input>
</process>
<if desc="Optional steps">
<cond>$check{output}</cond>
<then>
...
</then>
</if>
The collect
operation is used to process a provided text using an expression that defines one or more capturing groups. This
operation can be particularly powerful as it can collect data from both structured and unstructured data. Each matching group
is appended to a list
of string
elements in the sequence with which it was matched, otherwise resulting in an empty list
if no matches were made. Consider the following example to see how this can be used:
<!-- Define a firstname and lastname in an unstructured text block -->
<assign to="aText">"My firstname is 'John' and my lastname is 'Doe'."</assign>
<!-- Collect the data using an expression with two capturing groups -->
<process id="personData" handler="RegExpProcessor">
<operation>collect</operation>
<input name="input">$aText</input>
<input name="expression">".+ firstname is '([\w]+)' .+ lastname is '([\w]+)'"</input>
</process>
<!-- Prints "John" -->
<log>$personData{0}</log>
<!-- Prints "Doe" -->
<log>$personData{1}</log>
TemplateProcessor
Used to generate text data based on a provided template. Using this processing handler instead of the basic GITB TDL templating capabilities permits the decoupling of information in the test session context and the template, and also generation of complex content based on FreeMarker templates. This processor should be used for any template-based data generation that is not limited to simple placeholder replacements.
Operation |
Description |
Input(s) |
Output(s) |
---|---|---|---|
|
Process the provided template and parameters to produce the output. |
Yes |
Yes, a |
The input parameters expected by the process
operation are as follows:
Operation |
Input name |
Required? |
Description |
---|---|---|---|
|
|
Yes |
The template content to use (can be of any type that results in a |
|
|
No |
A |
|
|
No |
A |
The following example illustrates usage of the TemplateProcessor
to create a message based on a FreeMarker template:
<assign to="parameters{value1}">'Value to use'</assign>
<assign to="parameters{listValues}" append="true">1</assign>
<assign to="parameters{listValues}" append="true">2</assign>
<assign to="parameters{listValues}" append="true">3</assign>
<process output="message" handler="TemplateProcessor">
<input name="parameters">$parameters</input>
<input name="template">$freemarkerTemplateFile</input>
<input name="syntax">'freemarker'</input>
</process>
<log>$message</log>
In this example the “freemarkerTemplateFile” variable is set (e.g. via import) to a template with the following content:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<content>
<value>${value1}</value>
<items>
<#list listValues as listValue>
<item>${listValue}</item>
</#list>
</items>
</content>
</data>
Notice here how the template defines FreeMarker constructs (a list iteration) to go over the items of a collection named “listValues”. This was passed in the
“parameters” map
when calling the process step. When executed, and considering the example’s input, this step will produce data as follows:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<content>
<value>Value to use</value>
<items>
<item>1</item>
<item>2</item>
<item>3</item>
</items>
</content>
</data>
TokenGenerator
Used to generate tokens that can be used as data in test cases. This processing handler supports but does not require a processing transaction to be established. The following operations are supported:
Operation |
Description |
Input(s) |
Output(s) |
---|---|---|---|
|
Generate a random UUID text value matching a Java UUID (e.g. “123e4567-e89b-12d3-a456-556642440000”). This is a value that can be considered as unique for test purposes. |
No |
A |
|
Generate a timestamp for the current or a provided time based on a format string. |
Yes |
A |
|
Generate a text token with potentially fixed and/or random parts to match a provided regular expression. |
Yes |
A |
|
Generate a random integer of double precision number between optional minimum and maximum bounds. |
Yes |
A |
The input parameters expected by the different operations are as follows:
Operation |
Input name |
Required? |
Description |
---|---|---|---|
|
|
No |
An optional |
|
|
No |
An optional |
|
|
No |
The formatting pattern to apply provided as a |
|
|
No |
The timezone to consider when generating a formatted timestamp provided as a |
|
|
No |
A |
|
|
No |
A |
|
|
No |
The formatting pattern to use to interpret the |
|
|
No |
A |
|
|
Yes |
A regular expression acting as a template to determine the generated token’s format. |
|
|
No |
A |
|
|
No |
A |
|
|
No |
A |
A typical use case for the TokenGenerator
is to generate text tokens that can be used in test cases either as input parameters to
e.g. messaging calls (see Handler inputs and outputs) or as values to replace in loaded text templates (see Expressions and templates).
The uuid
operation provides a random and unique identifier where special formatting is not required (apart from an optional prefix and postfix),
whereas the timestamp
operation generates a timestamp string that includes date/time values but can also have fixed parts (e.g. if you need to generate
a text token with a fixed part and a variable part based on the current date/time). The string
operation can be used to
generate any kind of text token with both fixed and random parts following a pattern based on a provided regular expression. Finally, the random
operation
is used to generate random numbers that can be used as-is, or help in selecting random elements from a list
, or as part of XPath expressions.
Note
Default format for input dates: If a date
is provided without an inputFormat
, the pattern of dd/MM/yyyy'T'HH:mm:ss.SSSZ
is assumed
by default. Moreover, all parts are considered optional allowing you to specify only parts of a date, making use of the following defaults for those that are missing:
Day of year (
dd
): The 1st day of the month.Month (
MM
): The 1st month of the year (January).Year (
yyyy
): The current year.Time elements (
HH
,mm
,ss
andSSS
): A value of zero.Time zone (
Z
): UTC.
The examples that follow illustrate use of these operations to generate a series of tokens that are then presented to the user by means
of an interact step. Note in all cases how the produced value is retrieved from the map
resulting from the process
step
that is named based on the steps’ id
. The value itself is retrieved from within each map
under the value
key:
<!--
Generate a UUID (e.g. "971b4df9-4351-4cb8-9ba5-1f6373717ae0").
-->
<process id="uuid" handler="TokenGenerator">
<operation>uuid</operation>
</process>
<!--
Generate a UUID with a prefix and postfix (e.g. "message-971b4df9-4351-4cb8-9ba5-1f6373717ae0@my.org").
-->
<process id="uuid" handler="TokenGenerator">
<operation>uuid</operation>
<input name="prefix">"message-"</input>
<input name="postfix">"@my.org"</input>
</process>
<!--
Generate a timestamp for the current time without specifying formatting.
Example output would be "1560238501040".
-->
<process id="defaultTimestamp" handler="TokenGenerator">
<operation>timestamp</operation>
</process>
<!--
Generate a timestamp for the current time with provided formatting.
Example output would be "DATE[2019-05-22] TIME[11:48:06]".
-->
<process id="formattedTimestamp" handler="TokenGenerator">
<operation>timestamp</operation>
<input name="format">"'DATE['yyyy-MM-dd'] TIME['HH:mm:ss']'"</input>
</process>
<!--
Generate an XML timestamp for the current time.
-->
<process id="formattedTimestamp" handler="TokenGenerator">
<operation>timestamp</operation>
<input name="format">"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"</input>
</process>
<!--
Generate an XML timestamp for the current time but expressed in the GMT+2 timezone.
-->
<process id="formattedTimestamp" handler="TokenGenerator">
<operation>timestamp</operation>
<input name="format">"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"</input>
<input name="zone">"GMT+2"</input>
</process>
<!--
Generate a timestamp for the provided time and formatting.
The output would be "2014-05-11".
-->
<process id="formattedTimestampProvidedTime" handler="TokenGenerator">
<operation>timestamp</operation>
<input name="time">'1399792366000'</input>
<input name="format">"yyyy-MM-dd"</input>
</process>
<!--
Generate a timestamp for the current time minus one minute (600000 milliseconds) using the provided formatting.
Example output would be "2019-06-11 10:23:10".
-->
<process id="formattedTimestampDiff" handler="TokenGenerator">
<operation>timestamp</operation>
<input name="diff">-600000</input>
<input name="format">"yyyy-MM-dd HH:mm:ss"</input>
</process>
<!--
Obtain the current time (T) and then generate two timestamps:
- T minus one hour.
- T plus one hour.
-->
<process id="now" handler="TokenGenerator">
<operation>timestamp</operation>
</process>
<process id="nowMinusOneHour" handler="TokenGenerator">
<operation>timestamp</operation>
<input name="time">$now{value}</input>
<input name="diff">-3600000</input>
<input name="format">"yyyy-MM-dd HH:mm:ss"</input>
</process>
<process id="nowPlusOneHour" handler="TokenGenerator">
<operation>timestamp</operation>
<input name="time">$now{value}</input>
<input name="diff">3600000</input>
<input name="format">"yyyy-MM-dd HH:mm:ss"</input>
</process>
<!--
Generate a timestamp based on an existing date/time string plus one hour
-->
<process id="timestampFromFormattedString1" handler="TokenGenerator">
<operation>timestamp</operation>
<input name="date">'20-10-2021 13:30:00'</input>
<input name="inputFormat">'dd-MM-yyyy HH:mm:ss'</input> <!-- Assumes UTC -->
<input name="diff">3600000</input>
</process>
<!--
Generate a timestamp based on an existing date/time string plus one hour (default formatting)
-->
<process id="timestampFromFormattedString2" handler="TokenGenerator">
<operation>timestamp</operation>
<input name="date">'20/10'</input> <!-- Assumes the current year, midnight, and a UTC timezone -->
<input name="diff">3600000</input>
</process>
<!--
Generate a random string with 2 characters followed by 10 digits.
Example output would be "cD6723820231".
-->
<process id="stringRandom" handler="TokenGenerator">
<operation>string</operation>
<input name="format">"[a-zA-Z]{2}\d{10}"</input>
</process>
<!--
Generate a random string:
- Starting with "PREFIX" and ending with "POSTFIX".
- With random parts of (a) 5 digits, (b) 5 occurences of 'a', 'b' or 'c', and (c) 2 digits.
- With hyphens between all fixed and random parts.
Example output would be "PREFIX-32145-abcaa-02-POSTFIX".
-->
<process id="stringRandomAndFixed" handler="TokenGenerator">
<operation>string</operation>
<input name="format">"PREFIX-\d{5}-[abc]{5}-\d{2}-POSTFIX"</input>
</process>
<!--
Generate a random integer between 1 and 10 (exclusive).
-->
<process id="numberRandom" handler="TokenGenerator">
<operation>random</operation>
<input name="minimum">1</input>
<input name="maximum">10</input>
<input name="integer">true()</input>
</process>
<!--
Display all generated tokens to the user.
-->
<interact desc="Generated tokens">
<instruct desc="UUID:">$uuid{value}</instruct>
<instruct desc="The default timestamp:">$defaultTimestamp{value}</instruct>
<instruct desc="A formatted timestamp:">$formattedTimestamp{value}</instruct>
<instruct desc="A formatted timestamp for provided time:">$formattedTimestampProvidedTime{value}</instruct>
<instruct desc="A timestamp using a diff:">$formattedTimestampDiff{value}</instruct>
<instruct desc="Now minus one hour:">$nowMinusOneHour{value}</instruct>
<instruct desc="Now plus one hour:">$nowPlusOneHour{value}</instruct>
<instruct desc="Plus one hour of formatted string:">$timestampFromFormattedString1{value}</instruct>
<instruct desc="Plus one hour of default formatted string:">$timestampFromFormattedString2{value}</instruct>
<instruct desc="A random string:">$stringRandom{value}</instruct>
<instruct desc="A random string with fixed parts:">$stringRandomAndFixed{value}</instruct>
<instruct desc="A random number:">$numberRandom{value}</instruct>
</interact>
Note
Timestamps for use in XML content: Formatted timestamps generated for use in XML content should match the formatting
of the ISO 8601 version of the W3C XML Schema dateTime definition. The pattern to apply to get a XSD-valid timestamp is:
yyyy-MM-dd'T'HH:mm:ss.SSSXXX
.
XSLTProcessor
Used to transform XML content using an XSLT style sheet, both being provided as inputs, and output the result.
Operation |
Description |
Input(s) |
Output(s) |
---|---|---|---|
|
Process XML content using an XSLT style sheet and return the transformed result. |
Yes |
Yes, a |
The input parameters expected by the process
operation are as follows:
Operation |
Input name |
Required? |
Description |
---|---|---|---|
|
|
Yes |
The XML content to transform. |
|
|
Yes |
The XSLT style sheet to use for the transformation. |
The following example illustrates usage of the XSLTProcessor
to transform the provided “xmlContent” (the XML input) using the “xsltContent” (the XSLT style sheet).
These variables may be provided in any manner, for example the “xmlContent” could be uploaded via a user interaction step whereas the “xsltContent” could
be imported from the test suite’s resources.
<process output="result" handler="XSLTProcessor">
<input name="xml">$xmlContent</input>
<input name="xslt">$xsltContent</input>
</process>
<log>$result</log>
Built-in validation handlers
The title of each section corresponds to the name of the handler that needs to be configured in the relevant verify step’s handler
attribute.
ExpressionValidator
Used to verify whether a provided expression evaluates to “true”. The
ExpressionValidator
is the most generic validation handler as it can be used to check any arbitrary
condition.
Input name |
Required? |
Type |
Description |
---|---|---|---|
|
Yes |
The expression to evaluate. |
<verify handler="ExpressionValidator" desc="Validate UUID">
<input name="expression">$variable != "unwantedValue"</input>
</verify>
Note
The expression
input is not presented in the verify step’s validation report as it
would only ever display a “true” or “false”.
NumberValidator
Used to verify that a provided number
matches an expected value.
Input name |
Required? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The value to check. |
|
Yes |
|
The expected value. |
<verify handler="NumberValidator" desc="Check number">
<input name="actualnumber">$aNumber</input>
<input name="expectednumber">'10'</input>
</verify>
RegExpValidator
Used to verify that a provided string
matches a regular expression.
Input name |
Required? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The value to check. |
|
Yes |
|
The expression to match. |
<verify handler="RegExpValidator" desc="Check string">
<input name="input">$aString</input>
<input name="expression">'^REF\-\d+$'</input>
</verify>
The regular expression provided for the expression
input is expected to be provided using the syntax used by the Java language.
This syntax also supports expression flags provided in an embedded manner, within an expression.
<verify handler="RegExpValidator" desc="Check string">
<input name="input">$aString</input>
<!-- Same expression but executed in a case insensitive (?i) and multiline (?m) manner. -->
<input name="expression">'(?im)^REF\-\d+$'</input>
</verify>
SchematronValidator
Note
For Schematron validation consider using the XmlValidator handler which offering more features and flexibility.
Used to validate an XML document against a Schematron file.
Input name |
Required? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The Schematron file to use for the validation (XSTL or SCH). |
|
Yes |
|
The XML document to validate. |
|
No |
|
The type of Schematron file to consider ( |
|
No |
|
Whether or not to include in the step’s report the Schematron used for the validation (default is “true”). |
|
No |
|
Whether to sort findings by severity (“true”) or location in the input ( |
|
No |
|
Whether or not to include in the step’s report the assertion performed for each finding (default is |
Note
XSLT vs SCH Schematron files: XSLT versions of Schematron files are pre-processed files and offer significantly better performance for complex rule cases. In addition, if Schematron rules import other resources, use of XSLT files is required.
<verify handler="SchematronValidator" desc="Validate content">
<input name="xmldocument">$docToValidate</input>
<input name="schematron">$schematronFile</input>
<!-- Following inputs are optional. -->
<input name="showSchematron">false()</input>
<input name="sortBySeverity">true()</input>
<input name="showTests">true()</input>
</verify>
StringValidator
Used to verify that a provided string
matches an expected value.
Input name |
Required? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The value to check. |
|
Yes |
|
The expected value. |
<verify handler="StringValidator" desc="Check string">
<input name="actualstring">$aString</input>
<input name="expectedstring">'expected_string'</input>
</verify>
XmlMatchValidator
Used to validate an XML document by matching it against a provided template.
Input name |
Required? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The XML file to validate. |
|
Yes |
|
The XML file to consider as the validation’s template. |
|
No |
|
An optional list of paths provided as XPath expressions identifying sections of the XML to ignore. |
The matching process takes place by normalising whitespace, ignoring comments and tolerating naming differences in namespace prefixes. In addition,
texts of elements or attributes in the provided template
can be specified with the special value ?
. This means that any value will be allowed
for this element or attribute and will be ignored as part of the matching (e.g. to ignore random tokens, timestamps, or texts with no expected value).
In case you want to ignore complete XML sections you may use the ignoredPaths
attribute. This allows you to define one or more paths that identify
elements that will, themselves and for all children, be ignored. For each provided path the following constraints apply:
It must be formed as a namespace-aware XPath expression considering the namespace prefixes of the provided
template
.It must identify a specific element, rather than a set of elements, a text node or an attribute.
It must be a simple element-based path with no functions, selectors or wildcards.
The following example illustrates how this validator can be used:
<!--
Validate an XML file based on a provided template.
-->
<verify handler="XmlMatchValidator" desc="Validate content">
<input name="xml">$docToValidate</input>
<input name="template">$templateFile</input>
</verify>
<!--
Another validation that also defines a set of paths to ignore.
Variable "pathsToSkip" is of type list[string].
-->
<assign to="$pathsToSkip" append="true">"/x:Invoice/x:BillingInformation/y:Comments"</assign>
<verify handler="XmlMatchValidator" desc="Validate content">
<input name="xml">$docToValidate</input>
<input name="template">$templateFile</input>
<input name="ignoredPaths">$pathsToSkip</input>
</verify>
XmlValidator
Used to validate an XML document against an XML Schema (XSD) and/or zero or more Schematron files.
Input name |
Required? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The XML document to validate. |
|
No |
|
The XSD to validate the document’s structure against. |
|
No |
|
The list of Schematron files to validate the document’s content against. |
|
No |
|
The type of Schematron file to consider ( |
|
No |
|
Whether or not XSD errors should prevent validation from proceeding with Schematron validations (default is “true”). |
|
No |
|
Whether findings should be sorted by severity (“true”) or by location in the XML content ( |
|
No |
|
Whether or not the XSDs and/or Schematrons used for the validation should be included in the step’s report (default is “true”). |
|
No |
|
Whether or not the Schematron assertions applied should be displayed for each reported finding (default is |
Regarding inputs, if you need to supply a single Schematron file you don’t need to create a list
and pass it as such. You can
simply pass the Schematron file as-is and the test engine will automatically convert it to a single-element list
. Note that considering
that both the xsd
and schematron
inputs are optional, if you provide neither, the validation will simply succeed with an empty report.
The following examples illustrate how the XmlValidator
can be used in various scenarios:
<!--
Validate against an XSD.
-->
<verify handler="XmlValidator" desc="XML validation">
<input name="xml">$content</input>
<input name="xsd">$schema</input>
</verify>
<!--
Validate against a single Schematron file.
-->
<verify handler="XmlValidator" desc="XML validation">
<input name="xml">$content</input>
<input name="schematron">$schematron</input>
</verify>
<!--
Validate against two Schematron files.
-->
<assign to="schematrons" append="true">$schematron1</assign>
<assign to="schematrons" append="true">$schematron2</assign>
<verify handler="XmlValidator" desc="XML validation">
<input name="xml">$content</input>
<input name="schematron">$schematrons</input>
</verify>
<!--
Validate against an XSD and two Schematron files:
- Without stopping for XSD errors.
- Sorting findings by severity.
- Hiding the XSD and Schematrons used.
-->
<assign to="schematrons" append="true">$schematron1</assign>
<assign to="schematrons" append="true">$schematron2</assign>
<verify handler="XmlValidator" desc="XML validation">
<input name="xml">$content</input>
<input name="schematron">$schematrons</input>
<input name="stopOnXsdErrors">false()</input>
<input name="sortBySeverity">true()</input>
<input name="showValidationArtefacts">false()</input>
</verify>
When comparing with the similar XSDValidator and SchematronValidator, the XmlValidator
is more
flexible and simple to use. In addition, it allows a better fine-tuning of how validation steps are presented. If for example validating
XML content requires validation against an XSD and two Schematron files, using the XSDValidator and SchematronValidator
will present three distinct validation steps in the session execution diagram. Using the XmlValidator
you may still display each such
validation separately but you also have the option of making a single validation for all artefacts. Doing so is typically preferred because:
It presents a single logical step, rather than expose the different resources involved.
It aggregates all findings in a single report.
For the sake of comparison, the following examples illustrate how two distinct validations carried out with the XSDValidator and SchematronValidator
can be replicated via a single use of the XmlValidator
:
<!--
Using the XSDValidator and SchematronValidator.
-->
<verify handler="XSDValidator" desc="Validate content">
<input name="xmldocument">$docToValidate</input>
<input name="xsddocument">$schemaFile</input>
</verify>
<verify handler="SchematronValidator" desc="Validate content">
<input name="xmldocument">$docToValidate</input>
<input name="schematron">$schematronFile</input>
</verify>
<!--
Equivalent validation using the XmlValidator.
-->
<verify handler="XmlValidator" desc="Validate content">
<input name="xml">$docToValidate</input>
<input name="xsd">$schemaFile</input>
<input name="schematron">$schematronFile</input>
<input name="stopOnXsdErrors">false()</input>
</verify>
XPathValidator
Used to evaluate an XPath 3.0 expression against a provided XML document. The result of the expression needs to evaluate to a boolean (i.e. true for success or false for failure).
Input name |
Required? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The XML document upon which the XPath expression will be evaluated. |
|
Yes |
|
The XPath 3.0 expression passed as a string. |
An important note here is that the XPath expression passed in xpathexpression
is meant to be a string.
This means that to run an expression as-is you need to wrap it in quotes. This is because the content of
the input
element can also be an expression that you want to evaluate to give you the final expression to
use. The following example illustrates both cases:
<!--
Pass a string as the expression to use.
-->
<verify handler="XPathValidator" desc="Check document">
<input name="xmldocument">$myDocument</input>
<input name="xpathexpression">"contains(/toc/text(), 'string to look for')"</input>
</verify>
<!--
Evaluate an expression that will give you the final expression to use.
-->
<verify handler="XPathValidator" desc="Check document">
<input name="xmldocument">$myDocument</input>
<input name="xpathexpression">concat("contains(/toc/text()", ", 'string to look for')")</input>
</verify>
In the expressions you use for the validations (attribute xpathexpression
) you may also make use of XML namespaces. Doing so is actually
a best practice to ensure that you don’t have ambiguous results due to elements with the same local names. To use namespaces in expressions
you first need to define their prefixes in the test case’s namespaces section. Moreover, keep in mind that the
provided input (attribute xmldocument
) also supports expressions with namespaces when determining the XML content to apply the XPath
expression to (if e.g. you want to validate only a part of an XML document).
The following example illustrates how you can use namespace prefixes with your XPath expressions:
<testcase>
<!--
Declare the namespaces to be used.
-->
<namespaces>
<ns prefix="ns1">urn:specification:foo</ns>
<ns prefix="ns2">urn:specification:bar</ns>
</namespaces>
<steps>
<!--
Use the defined namespaces.
-->
<verify handler="XPathValidator" desc="Check document">
<input name="xmldocument">$myDocument</input>
<input name="xpathexpression">"/ns1:Foo/ns2:Bar/text() = 'EXPECTED'"</input>
</verify>
</steps>
</testcase>
XSDValidator
Note
For XSD validation consider using the XmlValidator handler which offering more features and flexibility.
Used to validate an XML document against an XML Schema (XSD) instance.
Input name |
Required? |
Type |
Description |
---|---|---|---|
|
Yes |
|
The XSD to validate the document against. |
|
Yes |
|
The XML document to validate. |
|
No |
|
Whether to include in the step’s report the XSD used for the validation (default is “true”). |
|
No |
|
Whether to sort findings by severity (“true”) or location in the input ( |
<verify handler="XSDValidator" desc="Validate content">
<input name="xmldocument">$docToValidate</input>
<input name="xsddocument">$schemaFile</input>
<!-- Following inputs are optional. -->
<input name="showSchema">false()</input>
<input name="sortBySeverity">true()</input>
</verify>
Deprecated built-in messaging handlers
Warning
The built-in messaging handlers listed here have been deprecated as they cannot be effectively customised and require the direct exposure of the internal test engine to systems under test.
Each following section defines a table with the information expected by each messaging handler. The meaning of this information is as follows:
Input: These are the inputs provided for the send step.
Output: These are the outputs returned from the receive step.
Actor configuration: These are configuration properties that will be automatically set for simulated actors using this handler.
Receive configuration: These are configuration properties expected by the receive step.
Send configuration: These are configuration properties expected by the send step.
Transaction configuration: These are configuration properties defined in the btxn or bptxn step.
The title of each section corresponds to the name of the handler that needs to be configured in the relevant step’s handler
attribute (in
send, receive or btxn steps).
HttpMessaging
Warning
This messaging handler is deprecated. Use instead the HttpMessagingV2 handler or a custom messaging handler if you have complex messaging needs.
Used to send or receive content over HTTP.
Element name |
Element type |
Required? |
Type |
Description |
---|---|---|---|---|
|
Input |
No |
|
The HTTP version to consider. |
|
Input |
No |
|
The |
|
Input |
No |
|
The HTTP request body’s bytes. |
|
Input |
No |
|
A |
|
Output |
No |
|
The HTTP method. |
|
Output |
No |
|
The HTTP version. |
|
Output |
No |
|
The HTTP request path. |
|
Output |
No |
|
The |
|
Output |
No |
|
The bytes of the received body. |
|
Output |
No |
|
A |
|
Output |
No |
|
The HTTP status code from the received response. |
|
Actor configuration |
Yes |
|
The host of the actor. |
|
Actor configuration |
Yes |
|
The listen port for the actor. |
|
Actor configuration |
No |
|
The request path for the request. |
|
Receive configuration |
No |
|
The status code for responses. |
|
Send configuration |
Yes |
|
The HTTP method to use when sending. |
|
Send configuration |
No |
|
The request path URI to send to. |
|
Send configuration |
No |
|
HTTP URI extension for the address. |
|
Send configuration |
No |
|
Status for responses. |
|
Transaction configuration |
No |
|
Whether or not connections should be over HTTP (default) or HTTPS. |
<btxn from="Actor1" to="Actor2" txnId="t1" handler="HttpMessaging"/>
<send id="dataSend" desc="Send data" from="Actor1" to="Actor2" txnId="t1">
<config name="http.method">"POST"</input>
<config name="http.uri">"/path/to/service"</input>
<input name="http_body">$binaryContent</input>
</send>
<receive id="dataReceive" desc="Receive data" from="Actor2" to="Actor1" txnId="t1">
<config name="status.code">"200"</input>
</receive>
<etxn txnId="t1"/>
Note
Isolating communications: When using a HttpMessaging
handler to receive communication from a SUT, the test bed dynamically starts listening on
a new port for incoming traffic. This port (along with the host) are presented to the test bed user upon test initiation so that he/she can configure
the SUT accordingly. To avoid unwanted communication being received on this port that is unrelated to the test session, the test bed will only
listen to requests originating from the SUT’s address, ignoring others originating from other sources. To achieve this, the test bed uses the
network.host
parameter configured for the SUT that needs to be provided by the tester as part of the SUT’s configuration before starting a test.
The value for the network.host
parameter must be set with the public IP Address of the SUT endpoint.
Using HTTPS
The HttpMessaging
handler can be used both for HTTP and (one-way) HTTPS connection. The default setting is connection over HTTP. Switching to
HTTPS is done at the level of the handler’s enclosing transaction and applies to all subsequent send or receive steps. Enabling HTTPS
is achieved by passing a configuration parameter named “http.ssl” with a value of true or false (case insensitive) as part of the begin transaction
step (step btxn). This must be provided at this point because it is needed when creating the sender and receiver implementation.
The following example illustrates its use:
<btxn from="sender" to="receiver" txnId="t1" handler="HttpMessaging">
<config name="http.ssl">true</config>
</btxn>
<send id="dataSend" desc="Send data" from="sender" to="receiver" txnId="t1">
<config name="http.method">POST</config>
<input name="http_body">$content</input>
</send>
Note that the value “true” in this example could also have been provided as a variable reference (e.g. $isHTTPS
) allowing a test case to remain unaffected
if the underlying communication needs to be over HTTP or HTTPS. This could be especially interesting in cases where the SoapMessaging
handler is used to
test SUT endpoints over which the test bed has no control over the underlying transport channel. In this case the “http.ssl” parameter could be set as part of
the system’s configuration, as in the following example (assuming an endpoint name of “sutInfo” and an endpoint parameter named “isHTTPS”):
<btxn from="sender" to="receiver" txnId="t1" handler="HttpMessaging">
<config name="http.ssl">$sutInfo{isHTTPS}</config>
</btxn>
Support for sending and receiving multipart form data
When receiving, a multipart message is detected if the ContentType
header contains a boundary part. The http_parts
output is a map
that contains:
http_parts{parts}
: A list of all parts in sequence.
http_parts{parts}{0}{header}
: The part’s header as a string.
http_parts{parts}{0}{content}
: The part’s content as a binary.
http_parts{partsByName}
: A map of parts by name (for easy lookup of named parts):
http_parts{partsByName}{NAME}{header}
: The part’s header as a string.
http_parts{partsByName}{NAME}{content}
: The part’s content as a binary.
When sending, if a http_body
input is present this takes precedence. If not, and a http_parts
input is provided, then a multipart request is created. The
http_parts
input is a list
of maps
(one map per part). To send a part as a file the file_name
property needs to be passed. Specifically the information
on a part is as follows:
http_parts{0}{name}
: The name of the part.
http_parts{0}{content_type}
: The mime type of the part (text/plain for simple text).
http_parts{0}{file_name}
: The name of the file to set for the part if this is a file/binary p
The following TDL example illustrates how to populate and send a multipart request with three parts (two file parts and one test part):
<imports>
<artifact type="schema" encoding="UTF-8" name="file1">testSuite1/artifacts/file1.xml</artifact>
<artifact type="binary" encoding="UTF-8" name="file2">testSuite1/artifacts/file2.zip</artifact>
</imports>
<variables>
<var name="parts" type="list[map]"/>
<var name="filePartInfo1" type="map"/>
<var name="filePartInfo2" type="map"/>
<var name="textPartInfo1" type="map"/>
</variables>
<actors>
...
</actors>
<steps>
<!--
Define first file part.
-->
<assign to="$filePartInfo1{name}" type="string">"file1"</assign>
<assign to="$filePartInfo1{content_type}" type="string">"text/xml"</assign>
<assign to="$filePartInfo1{file_name}" type="string">"file1.xml"</assign>
<assign to="$filePartInfo1{content}" type="binary">$file1</assign>
<!--
Define second file part.
-->
<assign to="$filePartInfo2{name}" type="string">"file2"</assign>
<assign to="$filePartInfo2{content_type}" type="string">"application/zip"</assign>
<assign to="$filePartInfo2{file_name}" type="string">"file2.zip"</assign>
<assign to="$filePartInfo2{content}" type="binary">$file2</assign>
<!--
Define a third text part.
-->
<assign to="$textPartInfo1{name}" type="string">"text1"</assign>
<assign to="$textPartInfo1{content_type}" type="string">"text/plain"</assign>
<assign to="$textPartInfo1{content}" type="string">"A simple text value"</assign>
<!--
Put all parts in a list.
-->
<assign to="$parts" append="true">$filePartInfo1</assign>
<assign to="$parts" append="true">$filePartInfo2</assign>
<assign to="$parts" append="true">$textPartInfo1</assign>
<!--
Send the request.
-->
<btxn from="Sender" to="Receiver" txnId="t1" handler="HttpMessaging"/>
<send desc="Send file" from="Sender" to="Receiver" txnId="t1">
<config name="http.method">POST</config>
<input name="http_parts">$parts</input>
</send>
<etxn txnId="t1"/>
</steps>
HttpsMessaging
Warning
This messaging handler is deprecated. Use instead the HttpMessagingV2 handler or a custom messaging handler if you have complex messaging needs.
Used to send or receive content over HTTPS.
Element name |
Element type |
Required? |
Type |
Description |
---|---|---|---|---|
|
Input |
No |
|
The |
|
Input |
No |
|
The HTTP request body’s bytes. |
|
Output |
No |
|
The HTTP method. |
|
Output |
No |
|
The HTTP version. |
|
Output |
No |
|
The HTTP request path. |
|
Output |
No |
|
The |
|
Output |
No |
|
The bytes of the received body. |
|
Output |
No |
|
The HTTP status code from the received response. |
|
Actor configuration |
Yes |
|
The host of the actor. |
|
Actor configuration |
Yes |
|
The listen port for the actor. |
|
Actor configuration |
No |
|
The request path for the request. |
|
Receive configuration |
No |
|
The status code for responses. |
|
Send configuration |
Yes |
|
The HTTP method to use when sending. |
|
Send configuration |
No |
|
HTTP URI extension for the address. |
|
Send configuration |
No |
|
Status for responses. |
<btxn from="Actor1" to="Actor2" txnId="t1" handler="HttpsMessaging"/>
<send id="dataSend" desc="Send data" from="Actor1" to="Actor2" txnId="t1">
<config name="http.method">"POST"</input>
<config name="http.uri.extension">"/path/to/service"</input>
<input name="http_body">$binaryContent</input>
</send>
<receive id="dataReceive" desc="Receive data" from="Actor2" to="Actor1" txnId="t1">
<config name="status.code">"200"</input>
</receive>
<etxn txnId="t1"/>
Note
Isolating communications: Handler HttpsMessaging
builds upon the mechanism of HttpMessaging to isolate test
session communications when receiving data. Check it’s documentation on what is needed to achieve this.
HttpProxyMessaging
Warning
This messaging handler is deprecated. Use instead the HttpMessagingV2 handler or a custom messaging handler if you have complex messaging needs.
Used to proxy HTTP requests and responses between two actors.
Element name |
Element type |
Required? |
Type |
Description |
---|---|---|---|---|
|
Input |
No |
|
The |
|
Output |
No |
|
The HTTP method. |
|
Output |
No |
|
The HTTP version. |
|
Output |
No |
|
The HTTP request path. |
|
Actor configuration |
Yes |
|
The host of the actor. |
|
Actor configuration |
Yes |
|
The listen port for the actor. |
|
Send configuration |
No |
|
Address of the proxied service. |
In this case the request_data
input map
is defined as a convenience considering that we will always be receiving
a call that we want to proxy to a final destination. The HTTP-related parameters to send to the destination need to match
the initial parameters received.
<btxn from="Actor1" to="Actor2" txnId="t1" handler="HttpProxyMessaging"/>
<receive id="receiveData" desc="Receive call" from="Actor1" to="Actor2" txnId="t1" />
<send desc="Send call" from="Actor2" to="Actor1" txnId="t1">
<config name="proxy.address">http://PROXIED_SERVICE_ADDRESS</config>
<input name="request_data" source="$receiveData" />
</send>
<etxn txnId="t1"/>
SoapMessaging
Warning
This messaging handler is deprecated. Use instead the SoapMessagingV2 handler or a custom messaging handler if you have complex messaging needs.
Used to send or receive payloads via SOAP web service calls.
Element name |
Element type |
Required? |
Type |
Description |
---|---|---|---|---|
|
Input |
No |
|
A |
|
Input |
Yes |
|
The SOAP envelope to send. |
|
Input |
No |
|
A |
|
Output |
No |
|
The HTTP headers received. |
|
Output |
Yes |
|
The received SOAP header. |
|
Output |
Yes |
|
The received SOAP body. |
|
Output |
Yes |
|
The received SOAP envelope. |
|
Output |
Yes |
|
The XML content of the received SOAP body. |
|
Output |
No |
|
A |
|
Output |
No |
|
The number of attachments received. |
|
Output |
No |
|
The HTTP status code from the received response. |
|
Actor configuration |
Yes |
|
The host of the actor. |
|
Actor configuration |
Yes |
|
The listen port for the actor. |
|
Actor configuration |
No |
|
The request path to send the SOAP request to. |
|
Receive configuration |
Yes |
|
SOAP Version. Can be 1.1 or 1.2. |
|
Send configuration |
Yes |
|
SOAP Version. Can be 1.1 or 1.2. |
|
Send configuration |
No |
|
Character set encoding. |
|
Send configuration |
No |
|
HTTP URI extension for the address. |
|
Transaction configuration |
No |
|
Whether or not connections should be over HTTP (default) or HTTPS. |
<btxn from="Actor1" to="Actor2" txnId="t1" handler="SoapMessaging"/>
<send id="dataSend" desc="Send data" from="Actor1" to="Actor2" txnId="t1">
<config name="soap.version">1.2</config>
<input name="soap_message">$soapMessage</input>
</send>
<receive id="dataReceive" desc="Receive data" from="Actor2" to="Actor1" txnId="t1">
<config name="soap.version">1.2</config>
</receive>
<etxn txnId="t1"/>
Using HTTPS
The SoapMessaging
handler can be used both over an HTTP and (one-way) HTTPS connection. The default setting is connection over HTTP. Switching to
HTTPS is done at the level of the handler’s enclosing transaction and applies to all subsequent send or receive steps. Enabling HTTPS
is achieved by passing a configuration parameter named “http.ssl” with a value of true or false (case insensitive) as part of the begin transaction
step (step btxn). This must be provided at this point because it is needed when creating the sender and receiver implementation.
The following example illustrates its use:
<btxn from="sender" to="receiver" txnId="t1" handler="SoapMessaging">
<config name="http.ssl">true</config>
</btxn>
<send id="dataSend" desc="Send data" from="sender" to="receiver" txnId="t1">
<config name="soap.version">$soapVersion</config>
<input name="soap_message">$soapMessage</input>
</send>
Note that the value “true” in this example could also have been provided as a variable reference (e.g. $isHTTPS
) allowing a test case to remain unaffected
if the underlying communication needs to be over HTTP or HTTPS. This could be especially interesting in cases where the SoapMessaging
handler is used to
test SUT endpoints over which the test bed has no control over the underlying transport channel. In this case the “http.ssl” parameter could be set as part of
the system’s configuration, as in the following example (assuming an endpoint name of “sutInfo” and an endpoint parameter named “isHTTPS”):
<btxn from="sender" to="receiver" txnId="t1" handler="SoapMessaging">
<config name="http.ssl">$sutInfo{isHTTPS}</config>
</btxn>
Note
Isolating communications: Handler HttpsMessaging
builds upon the mechanism of HttpMessaging to isolate test
session communications when receiving data. Check it’s documentation on what is needed to achieve this.
TCPMessaging
Warning
This messaging handler is deprecated. Define instead a custom messaging handler to cover your test case messaging needs.
Used to send or receive an arbitrary byte stream over TCP.
Element name |
Element type |
Required? |
Type |
Description |
---|---|---|---|---|
|
Input |
Yes |
|
The stream of bytes to send. |
|
Output |
Yes |
|
The stream of bytes received. |
|
Actor configuration |
Yes |
|
The host of the actor. |
|
Actor configuration |
Yes |
|
The listen port for the actor. |
<btxn from="Actor1" to="Actor2" txnId="t1" handler="TCPMessaging"/>
<send id="dataSend" desc="Send data" from="Actor1" to="Actor2" txnId="t1">
<input name="content">$binaryContent</input>
</send>
<receive id="dataReceive" desc="Receive data" from="Actor2" to="Actor1" txnId="t1"/>
<etxn txnId="t1"/>
UDPMessaging
Warning
This messaging handler is deprecated. Define instead a custom messaging handler to cover your test case messaging needs.
Used to send or receive arbitrary bytes over UDP.
Element name |
Element type |
Required? |
Type |
Description |
---|---|---|---|---|
|
Input |
Yes |
|
The stream of bytes to send. |
|
Output |
Yes |
|
The stream of bytes received. |
|
Actor configuration |
Yes |
|
The host of the actor. |
|
Actor configuration |
Yes |
|
The listen port for the actor. |
<btxn from="Actor1" to="Actor2" txnId="t1" handler="UDPMessaging"/>
<send id="dataSend" desc="Send data" from="Actor1" to="Actor2" txnId="t1">
<input name="content">$binaryContent</input>
</send>
<receive id="dataReceive" desc="Receive data" from="Actor2" to="Actor1" txnId="t1"/>
<etxn txnId="t1"/>
Reusable external handlers
The sections that follow list handler implementations that are maintained by the Test Bed team but that are not built-into the test engine itself. In all cases such handlers are defined as service references in the test steps that support them, and are available in two ways:
As a reusable service hosted on Test Bed infrastructure that can be used as-is.
As a Docker image registered on the Docker Hub that can be used to install a local instance of the service.
Messaging services
The following sections summarise reusable messaging services that can be used as handler implementations for messaging transactions, as well as directly in send and receive steps.
AS4 messaging
The AS4 messaging component allows integration with a Domibus eDelivery access point to send and receive messages over eDelivery using the AS4 protocol. This component allows you to:
Send a message using a prepared header and payload as inputs.
Check for an acknowledgement received for a given message (previously sent) based on a message identifier.
Receive a message based on an expected message identifier. This will poll the Domibus backend API until the specific message is received.
To use this component, pull the isaitb/asx-messaging-v4 Docker image to install it on your environment. The resulting service must be accessible by the Test Bed, and also have access to the relevant Domibus backend API that will handle the messaging.
To send a message use a send step with the following inputs:
as4.send.header
, the AS4 message XML header (of typestring
,object
orbinary
).as4.send.payload
, the list of payloads to send (of typelist[binary]
).
<steps>
<btxn from="sender" to="received" txnId="t1" handler="http://localhost:8080/ms/api/as4messaging?wsdl"/>
<assign to="header">$headerToUse</assign>
<assign to="payloads" append="true">$payloadToInclude</assign>
<send id="data" desc="Send message" from="sender" to="receiver" txnId="t1">
<input name="as4.send.header">$header</input>
<input name="as4.send.payload">$payloads</input>
</send>
<!-- Assigned message ID returned as "as4.send.messageId" -->
<log>$data{as4.send.messageId}</log>
<!--
Sent content returned as a map named "sentContent" including entries:
- "as4.send.header": The header of the message.
- "as4.send.payload": The list of payloads.
-->
<assign to="headerSent">$data{sentContent}{as4.send.header}</assign>
<assign to="firstPayloadSent">$data{sentContent}{as4.send.payload}{0}</assign>
<etxn txnId="t1"/>
</steps>
To check for a message’s acknowledgement use a receive step with the following inputs:
as4.receive.type
, set to “ack_check”.as4.receive.messageId
, the ID of the message to check for (of typestring
).
<steps>
<btxn from="sender" to="received" txnId="t1" handler="http://localhost:8080/ms/api/as4messaging?wsdl"/>
...
<send id="data" desc="Send message" from="sender" to="receiver" txnId="t1">
<input name="as4.send.header">$header</input>
<input name="as4.send.payload">$payloads</input>
</send>
...
<receive id="ackData" desc="Receive acknowledgement" from="receiver" to="sender" txnId="t1">
<input name="as4.receive.messageId">$data{as4.send.messageId}</input>
<input name="as4.receive.type">'ack_check'</input>
</receive>
<!--
The receive step returns a map of two entries:
- "as4.send.messageId": The message ID that was acknowledged.
- "as4.message.send.reason": The acknowledgement text.
Whether the step is a success or failure depends on the state returned and the service's "messaging.ackFailureStates" environment variable.
-->
<log>$ackData{as4.send.messageId}</log>
<log>$ackData{as4.message.send.reason}</log>
<etxn txnId="t1"/>
</steps>
To receive a message use a receive step with the following input:
as4.receive.messageId
, the ID of the message to lookup (of typestring
).
<steps>
<btxn from="sender" to="receiver" txnId="t1" handler="http://localhost:8080/ms/api/as4messaging?wsdl"/>
<receive id="data" desc="Receive message" from="sender" to="receiver" txnId="t1">
<input name="as4.receive.messageId">$messageId</input>
</receive>
<!--
The receive step returns a map of the following entries:
- "header": The message header (of type "object").
- "payload": A map containing the payload data. Each separate payload received is added in a separate map named "payload.N" (where N is a 1-based index). Each of these includes the following entries:
- "payload.id": The ID of the payload (of type string).
- "payload.contentType": The payload's content/mime type (of type string).
- "payload.content": The payload's data (of type binary).
-->
<assign to="firstReceivedPayload">$data{payload}{payload.1}{payload.content}</assign>
<etxn txnId="t1"/>
</steps>
Processing services
The following sections summarise reusable processing services that can be used as handler implementations for process steps.
ZIP processing
The ZIP processing service allows you to extract files from received ZIP archives, or ZIP-like archives such as ASiC containers. Using this service you can:
Obtain the table of contents of a provided archive.
Extract one or more files from the archive based on provided search criteria.
This component functions as a stateful processing service, with extraction operations carried out within the scope of a processing transaction. This allows the ZIP archive to be provided once to the service and then maintained as state across calls to efficiently carry out several extraction operations. The archive in question is removed once the processing transaction or the overall test session ends. You can use this component:
Locally, by pulling the isaitb/zip-processing Docker image.
As a service, by setting your handler to
https://www.itb.ec.europa.eu/zip/api/processing?wsdl
.
The operations supported by the service are listed in the following table:
Operation |
Description |
Input(s) |
Output(s) |
---|---|---|---|
|
Provide the ZIP archive to the service for subsequent extraction operations. |
Yes |
A |
|
Extract one or more files from the archive. |
Yes |
A |
The input parameters expected by the different operations are as follows:
Operation |
Input name |
Required? |
Description |
---|---|---|---|
|
|
Yes |
A |
|
|
Yes |
A |
|
|
No |
A |
|
|
No |
A |
The following test case sample illustrates how to use the service to extract a file from a ZIP archive:
<steps>
<!--
As a first step create processing transaction pointing to the service
-->
<bptxn txnId="t1" handler="https://www.itb.ec.europa.eu/zip/api/processing?wsdl"/>
<!--
Call the 'initialize' operation to pass the binary archiveContent as an input named 'zip'
-->
<process id="toc" txnId="t1" operation="initialize">
<input name="zip">$archiveContent</input>
</process>
<!--
Call the 'extract' operation to retrieve a file with an exact but not case-sensitive match
-->
<process output="zip" txnId="t1" operation="extract">
<input name="path">'META-INF/manifest.xml'</input>
<input name="match">'exact'</input>
<input name="case">'false'</input>
</process>
<!--
Use if needed the number of returned entries
-->
<log>"Extracted " || $zip{entries} || " file(s)"</log>
<!--
Use the extracted file (first match)
-->
<log>"Processing file " || $zip{entry}{0}{path} "..."</log>
<assign to="file">$zip{entry}{0}{content}</assign>
<!--
Close the processing transaction to release the processed archive.
-->
<etxn txnId="t1"/>
</steps>
Validation services
The following sections summarise reusable validation services that can be used as handler implementations for verify steps.
ASiC validator
The ASiC validation service allows you to validate ASiC containers. The validator currently supports two validation profiles you can consider in your tests:
base: The base ASiC container definition (considered as the default).
etendering: The rules relevant to the PEPPOL Business Interoperability Specifications (BIS).
You can use this component:
Locally, by pulling the isaitb/asic-validator Docker image.
As a service, by setting your handler to
https://www.itb.ec.europa.eu/asic/api/validation?wsdl
.
When used in a verify step this validator expects the following inputs:
Input name |
Required? |
Description |
---|---|---|
|
Yes |
A |
|
No |
A |
The following test case sample illustrates how to use the validator to verify an ASiC container using the default profile:
<steps>
<verify id="asicValidation" desc="Validate ASiC container" handler="https://www.itb.ec.europa.eu/asic/api/validation?wsdl">
<!-- The binary container to validate -->
<input name="file">$container</name>
<!-- An optional profile to apply. -->
<input name="profile">"base"</name>
</verify>
</steps>
CSV validator
The CSV validation service allows you to validate CSV content by means of one or more Table Schema definitions. It is the default, generic configuration of the Test Bed’s CSV validator component that expects the schemas to apply as inputs alongside the content to validate.
Note
The generic CSV validator is also available for standalone use via user interface, REST API and SOAP API. Furthermore, a custom validator with a predefinined configuration and specific settings can be defined following the Test Bed’s CSV validation guide. The API of such a custom instance is identical to the generic instance presented here.
You can use the CSV validator by one of two approaches:
Locally, by pulling the isaitb/csv-validator Docker image.
As a service, by setting your handler to
https://www.itb.ec.europa.eu/csv/soap/any/validation?wsdl
.
The validator supports several inputs to customise the validation to take place. The available inputs are listed in the service’s SOAP API documentation, where all listed inputs match exactly those that can be used in test cases through verify steps.
The following test case sample illustrates how to use the validator for the most common use case of validating JSON content against a schema:
<steps>
<!--
You can validate against any number of schemas in one go. In this case we use one schema (defined in $schema)
that is typically provided as an import but could also be loaded from configuration or even generated on the
fly in a previous test case step.
-->
<assign to="schema1{content}">$schema</assign>
<!-- Set embeddingMethod to "STRING" if the content is defined as a string ("BASE64" corresponds to binary). -->
<assign to="schema1{embeddingMethod}">"BASE64"</assign>
<assign to="schemasToUse" append="true">$schema1</assign>
<!--
Call the validator.
-->
<verify handler="https://www.itb.ec.europa.eu/csv/soap/any/validation?wsdl" desc="Validate CSV file">
<input name="contentToValidate">$fileToValidate</input>
<input name="schema">$schemasToUse</input>
<!-- Set embeddingMethod to "STRING" if the contentToValidate is defined as a string ("BASE64" corresponds to binary). -->
<input name="embeddingMethod">"BASE64"</input>
</verify>
</steps>
JSON validator
The JSON validation service allows you to validate JSON content by means of one or more JSON Schema definitions. It is the default, generic configuration of the Test Bed’s JSON validator component that expects the schemas to apply as inputs alongside the content to validate.
Note
The generic JSON validator is also available for standalone use via user interface, REST API and SOAP API. Furthermore, a custom validator with a predefinined configuration and specific settings can be defined following the Test Bed’s JSON validation guide. The API of such a custom instance is identical to the generic instance presented here.
You can use the JSON validator by one of two approaches:
Locally, by pulling the isaitb/json-validator Docker image.
As a service, by setting your handler to
https://www.itb.ec.europa.eu/json/soap/any/validation?wsdl
.
The validator supports several inputs to customise the validation to take place. The available inputs are listed in the service’s SOAP API documentation, where all listed inputs match exactly those that can be used in test cases through verify steps.
The following test case sample illustrates how to use the validator for the most common use case of validating JSON content against a schema:
<steps>
<!--
You can validate against any number of schemas in one go. In this case we use one schema (defined in $schema)
that is typically provided as an import but could also be loaded from configuration or even generated on the
fly in a previous test case step.
-->
<assign to="schema1{schema}">$schema</assign>
<!-- Set embeddingMethod to "STRING" if the content is defined as a string ("BASE64" corresponds to binary). -->
<assign to="schema1{embeddingMethod}">"BASE64"</assign>
<assign to="schemasToUse" append="true">$schema1</assign>
<!--
Call the validator.
-->
<verify handler="https://www.itb.ec.europa.eu/json/soap/any/validation?wsdl" desc="Validate JSON file">
<input name="contentToValidate">$fileToValidate</input>
<input name="externalSchemas">$schemasToUse</input>
<!-- Set embeddingMethod to "STRING" if the contentToValidate is defined as a string ("BASE64" corresponds to binary). -->
<input name="embeddingMethod">"BASE64"</input>
</verify>
</steps>
RDF validator
The RDF validation service allows you to validate RDF content via SHACL shapes definitions. It is the default, generic configuration of the Test Bed’s RDF validator component that expects the shapes to apply as inputs alongside the content to validate.
Note
The generic RDF validator is also available for standalone use via user interface, REST API and SOAP API. Furthermore, a custom validator with a predefinined configuration and specific settings can be defined following the Test Bed’s RDF validation guide. The API of such a custom instance is identical to the generic instance presented here.
You can use the RDF validator by one of two approaches:
Locally, by pulling the isaitb/shacl-validator Docker image.
As a service, by setting your handler to
https://www.itb.ec.europa.eu/shacl/soap/any/validation?wsdl
.
The validator supports several inputs to customise the validation to take place. The available inputs are listed in the service’s SOAP API documentation, where all listed inputs match exactly those that can be used in test cases through verify steps.
The following test case sample illustrates how to use the validator for the most common use case of validating RDF content against a shape graph:
<steps>
<!--
You can validate against any number of shape graph files in one go. In this case we use one file (defined in $shapes)
that is typically provided as an import but could also be loaded from configuration or even generated on the
fly in a previous test case step.
-->
<assign to="shapes1{ruleSet}">$shapes</assign>
<assign to="shapes1{ruleSyntax}">"application/turtle"</assign>
<!-- Set embeddingMethod to "STRING" if the content is defined as a string ("BASE64" corresponds to binary). -->
<assign to="shapes1{embeddingMethod}">"BASE64"</assign>
<assign to="shapesToUse" append="true">$shapes1</assign>
<!--
Call the validator.
-->
<verify handler="https://www.itb.ec.europa.eu/shacl/soap/any/validation?wsdl" desc="Validate RDF file">
<input name="contentToValidate">$fileToValidate</input>
<input name="contentSyntax">"application/rdf+xml"</input>
<input name="externalRules">$shapesToUse</input>
<!-- Set embeddingMethod to "STRING" if the contentToValidate is defined as a string ("BASE64" corresponds to binary). -->
<input name="embeddingMethod">"BASE64"</input>
</verify>
</steps>
XML validator
Note
Built-in validators for XML are also available, notably the XmlValidator for validation against XML Schema and Schematron, as well as the XmlMatchValidator for validation against expected templates.
The XML validation service allows you to validate XML content by means of one or more XML Schemas and Schematron. It is the default, generic configuration of the Test Bed’s XML validator component that expects the schemas and Schematrons to apply as inputs alongside the content to validate.
Note
The generic XML validator is also available for standalone use via user interface, REST API and SOAP API. Furthermore, a custom validator with a predefinined configuration and specific settings can be defined following the Test Bed’s XML validation guide. The API of such a custom instance is identical to the generic instance presented here.
You can use the XML validator by one of two approaches:
Locally, by pulling the isaitb/xml-validator Docker image.
As a service, by setting your handler to
https://www.itb.ec.europa.eu/xml/api/validation?wsdl
.
The validator supports several inputs to customise the validation to take place. The available inputs are listed in the service’s SOAP API documentation, where all listed inputs match exactly those that can be used in test cases through verify steps.
The following test case sample illustrates how to use the validator for the most common use case of validating XML content against an XML Schema and a Schematron rule file:
<steps>
<!--
You can validate against any number of schemas in one go. In this case we use one schema (defined in $schema)
that is typically provided as an import but could also be loaded from configuration or even generated on the
fly in a previous test case step.
-->
<assign to="schema1{content}">$schema</assign>
<!-- Set embeddingMethod to "STRING" if the content is defined as a string ("BASE64" corresponds to binary). -->
<assign to="schema1{embeddingMethod}">"BASE64"</assign>
<assign to="schemasToUse" append="true">$schema1</assign>
<!--
Prepare also the Schematron rules to use (defined in $schematron) that could similarly be imported, loaded from
configuration or generated on the fly.
-->
<assign to="schematron1{content}">$schematron</assign>
<!--
The Schematron type could be "xsl" (for Schematron transformed to XSLT), or "sch" for the raw Schematron format. Besides
experimentation or very simple cases, XSLT rules (so a "xsl" type value) should always be preferred.
-->
<assign to="schematron1{type}">"xsl"</assign>
<!-- Set embeddingMethod to "STRING" if the content is defined as a string ("BASE64" corresponds to binary). -->
<assign to="schematron1{embeddingMethod}">"BASE64"</assign>
<assign to="schematronsToUse" append="true">$schematron1</assign>
<!--
Call the validator.
-->
<verify handler="https://www.itb.ec.europa.eu/xml/api/validation?wsdl" desc="Validate XML file">
<input name="xml">$fileToValidate</input>
<input name="externalSchema">$schemasToUse</input>
<input name="externalSchematron">$schematronsToUse</input>
<!-- Set embeddingMethod to "STRING" if the contentToValidate is defined as a string ("BASE64" corresponds to binary). -->
<input name="embeddingMethod">"BASE64"</input>
</verify>
</steps>
Note
When using the generic XML validator you don’t need to always validate using XML Schema and Schematron. For example you could skip schema validation and only validate against a set of generated Schematron rules.
Custom external handlers
Custom service handlers are meaningful when you have project-specific testing needs that cannot be addressed by the test engine’s built-in capabilities or the existing reusable services offered by the Test Bed. In practice any non-trivial test setup would usually require at least a custom messaging service implementation to handle the messaging protocol foreseen by the project. This holds true even if a seemingly suitable built-in messaging handler is available, as you will most likely need to add customisations when making or receiving calls, but also adapt the reports produced by your messaging steps.
Custom service handler implementations would be defined in a custom web application that complements your test suites. This application would include implementations (as needed) of the GITB test service SOAP APIs that allow it to be orchestrated by the Test Bed. To guide you in the implementation of these APIs you can refer to the GITB test services documentation for:
Validation services, to validate content.
Messaging services, to send and receive messages.
Processing services, to implement supporting utility functions.
The starting point for the implementation is the Test Bed’s published template service. This is an executable template, allowing you to create new services based on existing demo starting implementations. Although simple, the pre-existing implementations fully cover the GITB service APIs and allow you to replace them with your own logic. Moreover, the documentation also includes a sample test case that illustrates how the demo service implementations can be used in test steps. For a guided, step-by-step tutorial on how to develop custom test services you can also check out the complex test development guide.
Authentication for external handlers
Handlers defined as external service implementations may need to be protected with access control. To support such protected services,
the GITB software foresees the possibility to authenticate as part of each service call. Authentication information needs to be configured
before any exchanges take place with the service and as such, cannot use the config
and input
elements otherwise used to pass information.
Authentication configuration is handled with property
elements that are used as part of the handler setup in:
The btxn step for transactional messaging services.
The send, receive and listen step for non-transactional messaging services.
The bptxn step for transactional processing services.
The process step for non-transactional processing services.
The verify step for validation services.
The authentication possibilities currently supported are:
Basic HTTP authentication for all calls to the service’s HTTP/HTTPS endpoint. This is authentication at the transport layer.
Authentication using the WS-Security UsernameToken profile (see here), supporting text and digest password transmission with timestamps and nonces. This is authentication at the SOAP application layer.
The properties that are supported in the property
elements are listed in the following table:
Property name |
Value |
Description |
---|---|---|
|
Any |
The username to provide when prompted for basic HTTP authentication. |
|
Any |
The password to provide when prompted for basic HTTP authentication. |
|
Any |
The username to include in the SOAP header as the UsernameToken’s username. |
|
Any |
The password to include in the SOAP header as the UsernameToken’s password. |
|
‘DIGEST’ (the default) or ‘TEXT’ |
The way the password is to be serialised in the header. ‘DIGEST’ includes it as a DIGEST whereas ‘TEXT’ adds it in plaintext. |
Note that use of HTTP basic authentication and the UsernameToken are not necessarily exclusive. A case where both are provided would be where a service protects access to its WSDL using HTTP basic authentication and adds additional protection for SOAP service calls by means of a UsernameToken. Combining both approaches is rare but possible. The following example illustrates use of these authentication properties calling various test services:
<!--
Transactional messaging service authentication with UsernameToken (DIGEST).
-->
<btxn from="Sender" to="Receiver1" txnId="t1" handler="$messagingServiceURL">
<property name="auth.token.username">$DOMAIN{serviceUsername1}</property>
<property name="auth.token.password">$DOMAIN{servicePassword1}</property>
<property name="auth.token.password.type">DIGEST</property>
</btxn>
<send id="dataSend" desc="Send message" from="Sender" to="Receiver1" txnId="t1"/>
<etxn txnId="t1"/>
<!--
Validation service authentication with UsernameToken (DIGEST - the default) and HTTP basic authentication.
-->
<verify handler="$validationService1" desc="Validate content">
<property name="auth.basic.username">$DOMAIN{serviceUsername2}</property>
<property name="auth.basic.password">$DOMAIN{servicePassword2}</property>
<property name="auth.token.username">$DOMAIN{serviceUsername3}</property>
<property name="auth.token.password">$DOMAIN{servicePassword3}</property>
<input name="content">$contentToValidate</input>
</verify>
<!--
Transactional processing service authentication with HTTP basic authentication.
-->
<bptxn txnId="t1" handler="$processingServiceURL">
<property name="auth.basic.username">$DOMAIN{serviceUsername4}</property>
<property name="auth.basic.password">$DOMAIN{servicePassword4}</property>
</bptxn>
<process id="result" txnId="t1">
<operation>action</operation>
<input name="anInput">$aValue</input>
</process>
<eptxn txnId="t1"/>
<!--
Non-transactional processing service authentication with HTTP basic authentication.
-->
<process id="result" handler="$otherProcessingServiceURL">
<property name="auth.basic.username">$DOMAIN{serviceUsername5}</property>
<property name="auth.basic.password">$DOMAIN{servicePassword5}</property>
<operation>action</operation>
<input name="anInput">$aValue</input>
</process>
<!--
Validation service authentication with UsernameToken (TEXT) authentication.
-->
<verify handler="$validationService2" desc="Validate content">
<property name="auth.token.username">$DOMAIN{serviceUsername6}</property>
<property name="auth.token.password">$DOMAIN{servicePassword6}</property>
<property name="auth.token.password.type">TEXT</property>
<input name="content">$contentToValidate</input>
</verify>
<!--
Non-transactional messaging service with UsernameToken (TEXT) authentication.
-->
<send id="dataSend" desc="Send message" from="Sender" to="Receiver" handler="$messagingServiceURL">
<property name="auth.token.username">$DOMAIN{serviceUsername7}</property>
<property name="auth.token.password">$DOMAIN{servicePassword7}</property>
<property name="auth.token.password.type">TEXT</property>
<input name="message">$messageToSend</input>
</send>
<etxn txnId="t1"/>
Handler inputs and outputs
The input
and output
elements used with handlers are what GITB refers to as “Binding elements”.
They share the following structure:
Name |
Required? |
Description |
---|---|---|
|
no |
The name of the input or output element. |
|
no |
The expression language that should be considered when evaluating its contained expression (see Expressions). |
|
no |
A pure variable reference identifying a source variable. Used as the target upon which to evaluate the contained expression. |
|
no |
Whether or not the result will be considered as a template for placeholder replacement (see Expressions and templates). By default this is “false”. |
The text content of the element is considered to be an expression (see Expressions). In the case a source
attribute is provided
the contained expression is evaluated on the variable identified by source
to produce the value. If no source
attribute is present the value
is the result of the expression itself. For inputs of type object
or schema
(i.e. XML documents) the source
attribute can also be used to pass
the complete document as the value. In this case use of the source
attribute to reference the relevant variable is equivalent to specifying its
reference as the expression:
<verify handler="XmlValidator" desc="Validate content">
<!--
Pass document through the expression.
-->
<input name="xml">$docToValidate</input>
<!--
Pass document through the source attribute.
-->
<input name="schematron" source="$schematronFile"/>
</verify>
Note
Specifying a fixed value: Considering that the default expression language is XPath, a fixed text value is provided by enclosing it in quotes. See Expressions for further details.
The input
and output
options for service handlers are documented as part of their module definition. For handlers accessible
via remote web service calls this information is returned when calling the handler’s getModuleDefinition
operation. This is also used internally
by the test bed before calling a service handler to ensure that required parameters are provided by the test case.