Guide: Basic test case messaging

Track

Test bed setup

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

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 and testCase3_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 the id 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 an id of receiveData. This is the name with which resulting step data will be stored in the test session context.

  • The receive step defines the uriExtension and method based on our specs, and will be used to match incoming requests.

  • The receive step also defines the body, headers and status 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 named endpoint. 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 imported purchaseOrder file) and headers (the created httpHeaders map variable) for the outgoing request.

  • The id of the send step is set to sendData. 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:

  1. Check that the response status was “200” (using a StringValidator).

  2. Check that the response content type was “text/plain” (using also a StringValidator).

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