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="XmlValidator" desc="Validate XML structure">
<input name="xml">$contentToValidate</input>
<input name="xsd">$schemaToUse</input>
</verify>
<verify handler="XmlValidator" desc="Validate business rules">
<input name="xml">$contentToValidate</input>
<input name="schematron">$schematronToUse</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 |
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 |
@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:
If the
output
element is non-empty, its content is used as an expression to calculate the return value.If the
output
element is empty thename
of theoutput
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="XmlValidator" desc="Validate XML structure">
...
</verify>
<verify id="schCheck" handler="XmlValidator" desc="Validate business rules">
...
</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:
The
desc
attribute of all steps.The
title
attribute of all steps with child steps (group, if, flow, foreach, repuntil, while).The
inputTitle
of user interaction steps (interact).The
from
andto
actor references in all messaging and interaction steps (btxn, send, receive, interact).The
reply
attribute of messaging steps (send, receive, listen).The
hidden
attribute of all steps (see also how this can be used to dynamically adapt the content of scriptlets).
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:
Define in the scriptlet’s params section the relevant input(s).
Reference the parameters within the scriptlet where you want to use them.
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.