Scriptlets

Overview

Scriptlets are reusable blocks of test steps that can be called during a test case’s execution. They are similar to function blocks in programming languages considering that:

  • They can receive inputs and may produce outputs.

  • They can define their own imports.

  • They operate in their own isolated scope.

Extracting reusable sections of test steps is a common need in test suites where certain set of tasks are frequently encountered. Consider for example a test suite that contains numerous test cases that at some point need to validate certain received data against the same common validation artefacts. Rather than repeating the same set of steps and artefact imports across all test cases, these validation steps can be defined in a scriptlet that is called wherever necessary. Scriptlets are defined as separate XML documents within a test suite, but can also be defined embedded within specific test cases.

The following example is a complete scriptlet that is used to validate a provided XML document against an XML Schema and a Schematron file. As output the scriptlet returns the name of the file’s root element:

<?xml version="1.0" encoding="UTF-8"?>
<scriptlet id="validateDocument" xmlns="http://www.gitb.com/tdl/v1/">
    <imports>
        <artifact type="schema" encoding="UTF-8" name="schemaToUse">resources/aSchemaFile.xsd</artifact>
        <artifact type="schema" encoding="UTF-8" name="schematronToUse">resources/aSchematronFile.sch</artifact>
    </imports>
    <params>
        <var name="contentToValidate" type="object"/>
    </params>
    <steps>
        <verify handler="XSDValidator" desc="Validate XML structure">
            <input name="xsddocument">$schemaToUse</input>
            <input name="xmldocument">$contentToValidate</input>
        </verify>
        <verify handler="SchematronValidator" desc="Validate XML content">
            <input name="schematron">$schematronToUse</input>
            <input name="xmldocument">$contentToValidate</input>
        </verify>
        <assign to="rootName" source="$contentToValidate">name(/*)</assign>
    </steps>
    <output name="rootName"/>
</scriptlet>

Scriptlets are used in test cases or other scriptlets by means of the call step. Considering the previous example, an example call from a test case would be as follows:

<!--
    Assume the scriptlet is defined in the same test suite in file "scriptlets/validateDocument.xml".
-->
<call id="call1" path="scriptlets/validateDocument.xml">
    <!--
        The variable "anXmlFile" contains the XML content to validate.
    -->
    <input name="contentToValidate">$anXmlFile</input>
</call>
<!--
    Log the XML file's root element name.
-->
<log>$call1{rootName}</log>

The following table provides an overview of the attributes and child elements that a scriptlet may have. A more detailed discussion per case follows in the subsequent sections.

Name

Required?

Description

@id

Yes

A unique identifier for the scriptlet.

metadata

No

A block containing the metadata used to describe the scriptlet.

namespaces

No

An optional set of namespaces to define the expression languages used in the scriptlet.

imports

No

An optional set of imports used to load additional resources.

params

No

An optional set of parameters that the scriptlet expects as input when called.

variables

No

An optional set of variables that are used locally within the scriptlet.

steps

Yes

The sequence of steps that this scriptlet foresees.

output

No

An optional set of output values that the scriptlet will return to its caller.

Elements

The following sections discuss the purpose and use of each element contained within the scriptlet definition.

Metadata

The structure and content of the metadata element is identical to the one defined for the test suite and its test cases. In the case of scriptlets however, no such metadata is displayed to users meaning that any information provided here is purely for test developers. In contrast to test suites and test cases, the metadata block for scriptlets is optional and can be fully skipped.

For further information on this element check the test case metadata documentation.

Namespaces

The namespaces element is identical in structure and purpose to the one defined for test cases. It is used to declare one or more namespace mappings (prefix to value), allowing the declared prefixes to be used in the scriptlet’s XPath expressions.

If the scriptlet is called by a test case or another scriptlet that already defines namespaces, these definitions are inherited. In case the current scriptlet defines a namespace prefix that has already been defined, then the scriptlet’s own definition overrides the inherited one.

For further information on this element check the test case namespaces documentation.

Imports

The imports element allows the use of arbitrary resources from the same or another test suite. The purpose and structure of this element is identical to the one defined for test cases. One notable difference in the case of scriptlets however, is that imports lacking an explicit from attribute are loaded from the test suite containing the scriptlet, which is not necessarily the same as the test suite containing the currently executing test case.

For further information on this element check the test case imports documentation.

Params

The params element is used to define the inputs that a scriptlet expects. Each such input is defined as a var element with the following structure:

Name

Required?

Description

@name

Yes

The name of the parameter. It is with this name that the parameter can be referenced within the scriptlet.

@type

Yes

The type of the parameter. One of the GITB data types can be used (see Types).

value

no

One or more values for the parameter acting as the parameter’s default value. More than one values are applicable in case of a map or list type.

Whenever a scriptlet is called using a call step, each of its declared parameters for which no default value has been defined, must be matched by a provided input. Parameters for which defaults are specified may also be provided as input, in which case the input overrides the default value.

Once provided, such parameters are available in the scope of the scriptlet and can be used in the same way as other variables. For scriptlets that are not embedded within test cases, using parameters provides state from the test session’s context to the scriptlet.

Variables

The variables element can be defined to create one or more variables that will be used during the scriptlet’s execution. The purpose and structure of this element is identical to the one defined for test cases. One notable point is that a scriptlet’s variables, as well as all other data in its scope, is isolated from the test case or scriptlet that is calling it, unless this is a scriptlet embedded within a test case.

For further information on this element check the test case variables documentation.

Steps

The steps element is where the scriptlet’s testing logic is implemented. It consists of a sequence of test steps, each realised by means of a GITB TDL step construct. The purpose and structure of this element is identical to the one defined for test cases, with the only notable difference being how actors are considered in messaging steps.

A test case’s actors represent the parties involved in the test. Each actor is assigned an id which is then referenced in messaging steps to determine the flow of communications. A scriptlet does not include such an actor definition block given that it is not the scriptlet’s role to determine which actors are simulated and which is the system under test (SUT). If a scriptlet includes messaging steps the test developer needs to ensure that the actor IDs that are referenced will match those defined by its calling test cases.

For further information on the scriptlet’s steps element and the different constructs it can include check the test case steps documentation.

Output

A scriptlet’s results are returned by the output elements that it defines. These outputs are the only means a scriptlet has to communicate information back to its caller, be it a test case or another scriptlet, but can be omitted in case no results are expected. The structure of each output element is as follows:

Name

Required?

Description

@name

Yes

A name with which the output can be referenced.

@lang

No

The expression language prefix to use to evaluate the contained expression (see Namespaces and Expressions).

@source

No

A variable reference to identify a source object variable upon which the contained expression should be evaluated.

@asTemplate

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 content of the output element is an expression that is used once the scriptlet’s processing is complete to calculate the output’s value. In the special case where a result needs to be calculated from an XPath expression on an XML document, the source attribute can be used to define the document to consider. The value returned by an output element is calculated as follows:

  1. If the output element is non-empty, its content is used as an expression to calculate the return value.

  2. If the output element is empty the name of the output element must match the name of a variable present in the scriptlet’s scope. If matched, the value of the variable is returned.

Taking the sample scriptlet presented previously, we want to return from the scriptlet a single output named “rootName” which is set with the validated document’s root element name. A first way to achieve this is to reference a variable from the scriptlet’s scope that contains the calculated value.

<scriptlet id="validateDocument" xmlns="http://www.gitb.com/tdl/v1/">
    <steps>
        ...
        <!--
            Calculate the document's root name and assign it to a variable named "rootName".
        -->
        <assign to="rootName" source="$contentToValidate">name(/*)</assign>
    </steps>
    <!--
        Reference the "rootName" variable from the scriptlet's scope.
    -->
    <output name="rootName"/>
</scriptlet>

Using this approach we treat the output element simply as a definition for which we need to ensure that, once complete, the scriptlet’s scope will include a similarly named variable. Note how the output element is itself empty given that it should not evaluate any expression to return its value. An alternative approach would be to use this expression to define the value as follows:

<scriptlet id="validateDocument" xmlns="http://www.gitb.com/tdl/v1/">
    <steps>
        ...
        <assign to="documentRootName" source="$contentToValidate">name(/*)</assign>
    </steps>
    <!--
        Reference the "documentRootName" variable from the scriptlet's scope.
    -->
    <output name="rootName">$documentRootName</output>
</scriptlet>

This uses a simple variable expression and is largely equivalent. We could of course provide a more elaborate expression here to e.g. enclose the value in square brackets:

<output name="rootName">concat('[', $documentRootName, ']')</output>

Finally, when using the output element’s expression to calculate its return value we could in our example skip the previous assign step altogether as follows:

<scriptlet id="validateDocument" xmlns="http://www.gitb.com/tdl/v1/">
    <steps>
        ...
    </steps>
    <!--
        Calculate the value to return in the output element itself.
    -->
    <output name="rootName" source="$contentToValidate">name(/*)</output>
</scriptlet>

Keep in mind that you may return any number of output elements you want. As an example, we could adapt our scriptlet to return also information on the result of the individual checks it made, in case the caller wants to take specific actions per case.

<scriptlet id="validateDocument" xmlns="http://www.gitb.com/tdl/v1/">
    <steps>
        <verify id="xsdCheck" handler="XSDValidator" desc="Validate XML structure">
            ...
        </verify>
        <verify id="schCheck" handler="SchematronValidator" desc="Validate XML content">
            ...
        </verify>
    </steps>
    <!--
        Return the name of the document's root element and the individual validation steps' results.
    -->
    <output name="rootName" source="$contentToValidate">name(/*)</output>
    <output name="isValidForSchema">$xsdCheck</output>
    <output name="isValidForSchematron">$schCheck</output>
</scriptlet>

In this example note how the verify steps are set with a specific id. Once each validation is completed the overall results are recorded as boolean variables which are then used to return the scriptlet’s outputs (see the verify step documentation for further details on this).

Note

Selecting outputs: When a scriptlet returns multiple outputs its caller may choose to select only a subset of these to use. For details on this and how returned outputs can be used check the call step’s documentation.

Dynamic presentation within scriptlets

Certain information relative to presenting test steps are expected to have fixed values that are known before the a test session is executed. Such information includes step descriptions, titles and actor references (e.g. used in messaging steps) which remain unchanged during a test session to allow a consistent execution diagram to be presented to the user.

This rule is relaxed in the case of scriptlets given that they can be used in various contexts. They can be used by multiple different test cases, potentially spanning several test suites, or even used from within other scriptlets (essentially anywhere you can have a call step). To enable this flexibility, the rule for otherwise constant values in scriptlets is that these can be set via variable references as long as their value can be determined when a test case’s definition is initially loaded.

The cases where otherwise fixed values can be set via variable reference are:

Values set in this way need to be provided as inputs to the scriptlet and resolve to constants before the test begins. In practical terms this means that you will need to:

  1. Define in the scriptlet’s params section the relevant input(s).

  2. Reference the parameters within the scriptlet where you want to use them.

  3. Pass as fixed values or configuration value references the values for the parameters when you call the scriptlet.

As an example of this consider the following scriptlet:

<scriptlet id="receiveData" xmlns="http://www.gitb.com/tdl/v1/">
    <params>
        <var name="descriptionToUse" type="string"/>
        <var name="from" type="string"/>
        <var name="to" type="string"/>
    </params>
    <steps>
        ...
        <receive id="receiveStep" desc="$descriptionToUse" from="$from" to="$to" txnId="t1">
            <input name="countryCode">$ORGANISATION{countryCode}</input>
        </receive>
        ...
    </steps>
</scriptlet>

The desc, from and to in this case are set dynamically based on the values passed as parameters. In a test case that will make use of this scriptlet we then add a call step as follows:

<call path="scriptlets/receiveData.xml">
    <input name="descriptionToUse">'Receive a message'</input>
    <input name="from">'Actor1'</input>
    <input name="to">'Actor2'</input>
</call>

Notice here how the parameters defined in the scriptlet are supplied with constant values. This allows the test engine to calculate a specific test execution graph when the test case is loaded but provides the flexibility for the scriptlet to be used in various scenarios.

Dynamic steps within scriptlets

Similar to dynamically changing labels and actors, a scriptlet can also have its contained steps be dynamically defined. This is achieved via specific boolean flags, covered in this section, that in the case of scriptlets can be set as variable references as long as their values can be determined at test case load time and remain constant during execution. Such flags, defining the visual representation of a test case, need to remain fixed given that a test case’s presentation cannot change during test execution.

Dynamically changing a scriptlet’s contained steps can be achieved via the following flags:

  • The hidden attribute supported by all test steps. This allows you to hide steps while ensuring they will still be executed.

  • The static attribute of if steps. This allows you to conditionally include steps, ensuring that non-included steps are not executed.

In the case of the hidden attribute, setting this to true will result in a step being executed but not displayed. Setting this to a variable reference is allowed only within scriptlets in which case the variable needs to match one of the scriptlet’s parameters. The variable reference is evaluated when the test case is loaded, with the resulting value being determined either from the scriptlet’s inputs (provided via its call step) or the default value defined for the parameter.

The if step’s static attribute goes further, allowing you to fully skip sets of steps. In this case, when static is set to true the test engine will expect to find the step’s condition (its cond element) set with a variable reference. It is this variable reference that is then evaluated at load time to determine whether to include the steps in the then block (if true), or the else block (if false and if defined). One important additional point here is that a statically evaluated if step does not display a boundary box and title, but rather directly displays the steps of the selected branch. This means that you can use a static if as other languages use “include” and “import” constructs.

Note

A similar result to an if step defined as static can be achieved by using a hidden if step with an explicitly visible then block. Check the if step’s documentation for more details.

The cases described above are presented in the sample scriptlet below to highlight their use.

<scriptlet id="receiveData" xmlns="http://www.gitb.com/tdl/v1/">
    <params>
        <!-- Parameter that must be specified as an input. -->
        <var name="hideMessage" type="boolean"/>
        <!-- Parameter that may be optionally specified as an input. -->
        <var name="validateAsExpression" type="boolean">
            <value>true</value>
        </var>
    </params>
    <steps>
        ...
        <!-- If 'hideMessage' is set to true, this message exchange will take place but will not be displayed. -->
        <receive hidden="$hideMessage" id="receiveStep" desc="Receive message" from="Actor1" to="Actor2" txnId="t1">
            <input name="countryCode">$ORGANISATION{countryCode}</input>
        </receive>
        <!--
            Static 'if' to determine the steps used to process the received message.
            No 'if' boundary will be displayed and the steps of the rejected branch will be excluded.
        -->
        <if static="true">
            <cond>$validateAsExpression</cond>
            <then>
                <log>"Validating input as a TDL expression"</log>
                <verify handler="ExpressionValidator" desc="Validate value">
                    <input name="expression">$value = $expectedValue</input>
                </verify>
            </then>
            <else>
                <log>"Validating input as a regular expression"</log>
                <verify handler="RegExpValidator" desc="Validate value">
                    <input name="input">$value</input>
                    <input name="expression">'^REF\-\d+$'</input>
                </verify>
            </else>
        </if>
        ...
    </steps>
</scriptlet>

As you see the scriptlet expects two parameters, hideMessage and validateAsExpression, that determine the display and content of the scriptlet. They are used respectively in the receive step’s hidden attribute, and the condition of the if step that is marked as static.

When calling this scriptlet we need to ensure that both these variables can be evaluated at load time. The validateAsExpression parameter already has a default value, so a call step needs to only override it if needed. The following example illustrates this (note how false() is used as opposed to false given that this is an expression).

<call path="scriptlets/receiveData.xml">
    <input name="hideMessage">false()</input>
    <!--
        No need to specify 'validateAsExpression' as true given
        that this is already the default.

        <input name="validateAsExpression">true()</input>
    -->
</call>

Scriptlets embedded in test cases

Scriptlets are typically defined as separate XML documents within a test suite that can be used across its test cases or externally from other test suites. It is also possible however, to define scriptlets within a specific test case in its scriptlets element. Such embedded scriptlets are processed in the same way but have certain key differences:

  • Scope inheritance: The scope of an embedded scriptlet extends, without replacing, the scope of its included test case. This means that looking up a variable within the scriptlet will first check the scriptlet’s scope and, if not found, will check the test case’s scope. Across multiple embedded scriptlets however, scopes remain isolated.

  • Private access: Embedded scriptlets are only usable from the test case that contains them and from its other embedded scriptlets.

  • Identification: Embedded scriptlets rely on their id attribute to be identified and referenced in call steps. As such, this must be unique within the scope of their containing test case.

For more details on defining embedded scriptlets check the documentation of the test case’s scriptlets element. For information on how a scriptlet, embedded or not, is called check the documentation of the call step.