Guide: Basic test case messaging
Track |
---|
This guide walks you through the process of including messaging in GITD TDL test cases.
Note
This guide is based on using built-in messaging handlers in test cases. If you have more complex messaging needs you can follow instead Guide: Developing complex tests that explains the usage of external custom messaging handlers, and guides you through most aspects of complex test case development.
What you will achieve
At the end of this guide you will have created a GITB TDL test suite that includes two test cases that handle:
Receiving content sent via HTTP.
Sending content over HTTP.
Validating the exchanged content as well as additional communication properties.
The resulting test suite will be self-contained, requiring minimal configuration in the test bed to be used.
What you will need
About 15 minutes.
An XML or text editor.
A tool to create ZIP archives.
A basic understanding of XML and XML Schema (XSD).
A basic understanding of HTTP requests.
The GITB TDL documentation (optionally, to lookup further documentation and examples).
How to complete this guide
This guide builds upon the testing scenario and resulting test suite from Guide: Creating a test suite. If you have not already completed this, you should check its section Step 1: Define your testing needs to understand at least the testing scenario being considered. In terms of the test cases you will create in the current guide these serve as extensions to the previously created test suite. You may:
Follow the Steps of the previous guide to create the test suite step by step, or;
Skip them by downloading the test suite to use from here.
If you choose to skip the previous steps make sure you extract the test suite archive into a folder named testSuite1
. The contents
of this folder following the extraction should be as follows:
testSuite1
├── resources
│ └── PurchaseOrder.xsd
├── tests
│ └── testCase1.xml
└── testSuite.xml
Steps
Carry out the following steps to complete this guide.
Step 1: Define your testing needs
In Guide: Creating a test suite we assumed that you are part of a project to define a new specification for the exchange of purchase orders between EU retailers. Up to this point your project’s testing needs have focused only on the content itself without addressing how purchase orders are actually exchanged.
Your project’s experts have now also introduced a web service interface to make this exchange consistent. Retailer systems will need to implement this to receive orders but also be able to call it to send orders to other retailers. In terms of requirements:
The interface is a REST service listening for
HTTP POST
requests to a request path/receiveOrder
.The body of these requests is the purchase order XML.
The response to requests must contain only a reference identifier of the form
REF-[\d]+
, i.e. “REF-”” followed by one or more digits.
As an example, a valid request would be as follows:
POST /receiveOrder HTTP/1.1
...
Content-Type: text/xml
<?xml version="1.0"?>
<purchaseOrder xmlns="http://itb.ec.europa.eu/sample/po.xsd" orderDate="2018-01-22">
...
</purchaseOrder>
A valid response to such a request would be:
HTTP/1.1 200 OK
...
Content-Type: text/plain
REF-0123456789
Based on this addition to the specification, your project’s testing needs have also evolved. The conformance testing services offered up to now included a service for developers to upload purchase orders for validation. These will now be extended to cover:
Receiving purchases orders from retailer systems and validating the requests.
Sending purchase orders to retailer systems and validating the responses.
Step 2: Design your test suite
Up to this point you had defined a single Retailer actor for use in your test suite. A second actor named Simulated retailer will be added to support the new communication needs. Although not defined in the specification, this actor is needed since our test cases need two distinct actors to carry out messaging. The Simulated retailer will never of course be defined as the SUT (System Under Test).
In terms of test suite structure, we will update the existing test suite rather than create a new one. You will do so by complementing the existing test case with two additional ones, one to receive a purchase order and the other to send it. Note that you could define only a single test case both for sending and receiving but doing so would suggest that it is a messaging sequence defined as an expected process in the specification. Since there is no such process defined, the cleaner approach is to create distinct test cases as this allows conformance testing to focus separately on sending and receiving.
To better understand this choice, consider a retailer system that has successfully implemented the service interface (i.e. it can receive purchase orders) but has not yet been able to send them to other retailers. If you define a single test case that e.g. first expects the system to receive a purchase order and then to send one, this would fail even though the receiving part is successful.
Regarding the purchase orders sent by the test bed to retailer systems, we will assume for simplicity use of a fixed one that will be bundled in the test suite.
Step 3: Update the test suite file
Edit file testCase1/testSuite.xml
by adding the highlighted lines:
<?xml version="1.0" encoding="UTF-8"?>
<testsuite id="testSuite1" xmlns="http://www.gitb.com/tdl/v1/" xmlns:gitb="http://www.gitb.com/core/v1/">
<metadata>
<gitb:name>[TS1] Purchase order validation tests</gitb:name>
<gitb:description>Test suite to validate the EU purchase order specification.</gitb:description>
<gitb:version>1.0</gitb:version>
</metadata>
<actors>
<gitb:actor id="Retailer">
<gitb:name>Retailer</gitb:name>
<gitb:desc>The EU retailer system that needs to be capable of producing and processing purchase orders.</gitb:desc>
</gitb:actor>
<gitb:actor id="SimulatedRetailer">
<gitb:name>Simulated retailer</gitb:name>
<gitb:desc>A simulated EU retailer system used for testing purposes.</gitb:desc>
</gitb:actor>
</actors>
<testcase id="testCase1_upload"/>
<testcase id="testCase2_send"/>
<testcase id="testCase3_receive"/>
</testsuite>
In brief:
We added the
SimulatedRetailer
actor as the party exchanging with the SUT.We defined two new test cases
testCase2_send
andtestCase3_receive
as part of the test suite that you will create next.
Step 4: Create the test case for sending
The next step is to create the test case that will require the retailer’s system (i.e. the SUT) to send a purchase order to the test bed (i.e. the simulated retailer).
Within the test suite folder create file testCase2.xml
:
testSuite1
├── resources
│ └── PurchaseOrder.xsd
├── tests
│ ├── testCase1.xml
│ └── testCase2.xml
└── testSuite.xml
The content of testCase2.xml
should be as follows:
<?xml version="1.0" encoding="UTF-8"?>
<testcase id="testCase2_send" xmlns="http://www.gitb.com/tdl/v1/" xmlns:gitb="http://www.gitb.com/core/v1/">
<metadata>
<gitb:name>[TC2] Send purchase order</gitb:name>
<gitb:version>1.0</gitb:version>
<gitb:description>Test case to test that an EU retailer system can correctly send a purchase order.</gitb:description>
</metadata>
<imports>
<artifact type="schema" encoding="UTF-8" name="purchaseOrderXSD">resources/PurchaseOrder.xsd</artifact>
</imports>
<actors>
<gitb:actor id="Retailer" name="Retailer" role="SUT"/>
<gitb:actor id="SimulatedRetailer" name="Simulated retailer"/>
</actors>
<steps>
<assign to="httpHeaders{Content-Type}">"text/plain"<assign>
<receive id="receiveData" desc="Receive request" from="Retailer" to="SimulatedRetailer" handler="HttpMessagingV2">
<input name="uriExtension">"/receiveOrder"</input>
<input name="method">"POST"</input>
<input name="status">200</input>
<input name="body">"REF-0123456789"</input>
<input name="headers">$httpHeaders</input>
</receive>
<verify handler="XmlValidator" desc="Validate purchase order">
<input name="xml">$receiveData{request}{body}</input>
<input name="xsd">$purchaseOrderXSD</input>
</verify>
</steps>
<output>
<success>
<default>"Test session succeeded."</default>
</success>
<failure>
<default>"Test session failed. Refer to the failed step's report for more details."</default>
</failure>
</output>
</testcase>
This test case specifies the following:
Its metadata, similar to what you defined as part of the first test case. Note
testCase2_send
set as theid
matches it with the test suite.The XSD to use for the validation is imported as
purchaseOrderXSD
.The
steps
section defines the required messaging and validation logic.The
output
section contains customisable user-friendly messages on the overall output.
A first important point to highlight is the use of a receive
step with the HttpMessagingV2 handler. This is a built-in messaging handler that
allows you to make and receive calls over HTTP. Notice that the step also sets the from
and to
attributes to identify the direction of the
communication using the defined actor identifiers. Some additional important points to note are:
The initial
assign
step prepares the request headers setting the content type.We use for the
receive
step anid
ofreceiveData
. This is the name with which resulting step data will be stored in the test session context.The
receive
step defines theuriExtension
andmethod
based on our specs, and will be used to match incoming requests.The
receive
step also defines thebody
,headers
andstatus
for the response to be produced.
Following the messaging step the test case proceeds with validation. We check the payload of the received request against the purchase order XSD
using an XmlValidator. It is interesting to highlight the use of the receiveData
context variable that stores the received request message
as $receiveData{request}{body}
. Translating this expression this means that we look up the receiveData
map holding the result of the step, which includes
a nested map named request
with the properties of the received request, one of which is called body
for the request body.
When executing the test case, the test bed will consider the inputs of the receive
step and listen for a HTTP POST at path:
http://localhost:8080/itbsrv/api/http/SYSTEM_API_KEY/receiveOrder
The placeholder SYSTEM_API_KEY
will be set to the unique API key of the system.
Note that the expected call and the test bed’s exposed listen address will also be listed in the test session’s log:
...
[2024-06-14 16:31:35] INFO - Waiting to receive POST at [http://localhost:8080/itbsrv/api/http/7039EC8EX4786X4103XB40FXC7242D11E9B0/receiveOrder] for step [Receive request]
Step 5: Create the test case for receiving
What remains is to complete the test case that will allow a retailer’s system to receive a purchase order sent by the test bed (i.e. the simulated retailer).
Within the test suite folder create file testCase3.xml
:
testSuite1
├── resources
│ └── PurchaseOrder.xsd
├── tests
│ ├── testCase1.xml
│ ├── testCase2.xml
│ └── testCase3.xml
└── testSuite.xml
The content of testCase3.xml
should be as follows:
<?xml version="1.0" encoding="UTF-8"?>
<testcase id="testCase3_receive" xmlns="http://www.gitb.com/tdl/v1/" xmlns:gitb="http://www.gitb.com/core/v1/">
<metadata>
<gitb:name>[TC3] Receive purchase order</gitb:name>
<gitb:version>1.0</gitb:version>
<gitb:description>Test case to test that an EU retailer system can correctly receive a purchase order.</gitb:description>
</metadata>
<imports>
<artifact type="binary" encoding="UTF-8" name="purchaseOrder">resources/sample.xml</artifact>
</imports>
<actors>
<gitb:actor id="Retailer" name="Retailer" role="SUT"/>
<gitb:actor id="SimulatedRetailer" name="Simulated retailer"/>
</actors>
<steps>
<assign to="httpHeaders{Content-Type}">"text/xml"</assign>
<send id="sendData" desc="Send request" from="SimulatedRetailer" to="Retailer" handler="HttpMessagingV2">
<input name="uri">$SYSTEM{endpoint}</input>
<input name="method">"POST"</input>
<input name="body">$purchaseOrder</input>
<input name="headers">$httpHeaders</input>
</send>
<verify handler="StringValidator" desc="Validate status">
<input name="actualstring">$sendData{response}{status}</input>
<input name="expectedstring">"200"</input>
</verify>
<verify handler="StringValidator" desc="Validate content type">
<input name="actualstring">$sendData{response}{headers}{Content-Type}</input>
<input name="expectedstring">"text/plain"</input>
</verify>
<verify handler="RegExpValidator" desc="Validate reference identifier">
<input name="input">$sendData{response}{body}</input>
<input name="expression">"^REF\-\d+$"</input>
</verify>
</steps>
<output>
<success>
<default>"Test session succeeded."</default>
</success>
<failure>
<default>"Test session failed. Refer to the failed step's report for more details."</default>
</failure>
</output>
</testcase>
A notable change here is that we no longer import the PurchaseOrder.xsd
file as we don’t plan on validating received
purchase orders. Validation in this case takes place on the identifier that is returned from the retailer’s system. What is imported (as variable purchaseOrder
)
is the sample purchase order that the test bed will be sending.
The sample you will use is available here
. Download this, and place it in folder testSuite1/resources
ensuring
that its name is sample.xml
.
testSuite1
├── resources
│ ├── PurchaseOrder.xsd
│ └── sample.xml
├── tests
│ ├── testCase1.xml
│ ├── testCase2.xml
│ └── testCase3.xml
└── testSuite.xml
Note
Changing file contents at runtime: The sample.xml
file you included in the test suite contains fixed content and will be used as-is. A more flexible approach
could be to use this as a template file with variable parts that will be replaced during test execution using values
from the session context. More information on how this works is available in the Expressions and template files section of the GITB TDL documentation.
Lets take a closer look now at the steps involved in this test case. The purchase order is sent using a send
step that uses the HttpMessagingV2 handler.
This defines the to
and from
attributes to match the communication direction for this test case. Some additional important points to note are:
The
uri
, set with the address to call which in this case will be provided as a system-level configuration property namedendpoint
. This will need to be created for the community, set as “required” and “included in tests”, and then be configured for the system under test.The definition of the
method
(set as “POST”),body
(the importedpurchaseOrder
file) andheaders
(the createdhttpHeaders
map variable) for the outgoing request.The
id
of thesend
step is set tosendData
. This will record the results of the step for subsequent use.
Note
Setting the endpoint address: Keep in mind that the provided address needs to be accessible by the test engine which is running as a Docker container. If
you are running both the test bed and the SUT on your localhost you will need to use host.docker.internal
in place of localhost
when
configuring the value. Note that this special address is Windows-specific - if on Linux use 172.17.0.1
.
For example, if your SUT’s endpoint is listening at http://localhost:9595/app/receiveOrder
you should use instead http://host.docker.internal:9595/app/receiveOrder
.
Once the messaging is complete the next step is to validate the response. We make three validations here in sequence using verify
steps:
Check that the response status was “200” (using a StringValidator).
Check that the response content type was “text/plain” (using also a StringValidator).
Check that the response body included a valid identifier (using a RegExpValidator).
For each of these checks we look up the returned data by referring to the sendData
variable. Considering for example the header check where we use
the expression $sendData{response}{headers}{Content-Type}
, we are referring to the step’s output map named sendData
, within which we refer to the response
map that contains the response properties, from which we pick the header
for the HTTP headers, of which finally we look up the Content-Type
.
Step 6: Package the test suite
At this point you have made all necessary updates to your test suite. To recap, the structure of your test suite folder needs to match the following:
testSuite1
├── resources
│ ├── PurchaseOrder.xsd
│ └── sample.xml
├── tests
│ ├── testCase1.xml
│ ├── testCase2.xml
│ └── testCase3.xml
└── testSuite.xml
To complete the creation of your test suite, ZIP the contents of the testSuite1
folder, without including the testSuite1
folder itself. Name the ZIP
archive testSuite1_v2.zip
(the resulting archive should match the one on GitHub, also available to download here).
Your updated test suite is now ready to be uploaded to the test bed. You can do this through the user interface, or using the test bed’s REST API. You can also find here a scripting approach using the REST API to reduce as much as possible development overhead.
Summary
Congratulations! You just created a test suite that supports sending and receiving content. Through its test cases you learned how to use the GITB TDL’s messaging steps as well as new built-in handlers for HTTP messaging and validation. You also saw an example of importing a sample file to send to the test system.
See also
As usual, make sure you check out the complete GITB TDL documentation for additional information on the GITB TDL and the possibilities you have in realising your testing needs.
A good next step from here would be to check out Guide: Defining your test configuration on how to setup the test suite on the test bed. If you don’t have a test bed instance to experiment with you can follow Guide: Installing the test bed for development use to easily install one locally for development purposes.
For more complex testing needs you may find the HttpMessagingV2 handler limiting. For example, you may want to customise the produced step reports, adapt responses based on received requests, or fine-tune communication channel security aspects. You may also need to carry out messaging that is unrelated to HTTP or any of the other built-in handlers. In such cases you should have a look at Guide: Developing complex tests that walks you through the definition and use of external custom messaging handlers, with which you can have full control over all messaging aspects.