Variables
There is a brief
overview video
available covering the most important aspects of variable handling in QF-Test.
Variables are the primary means to add flexibility to a test suite. Though they are used mainly as parameters for Procedures, they are also useful in many other cases.
Variables can be used in all attributes with text input fields.
Many checkboxes can be converted to text input through the button
at the top left. Then you can insert a boolean value either directly or via a variable.
Variable references
There are multiple ways to reference variables:
Referencing simple variables
$(variable name)
returns the string value of a variable.
If the value is not a string but is evaluated as part of a string or the result is used as text, the text representation of the value is used instead.
Referencing group variables
${group:name}
accesses a variable in a variable group.
Use it to access variables in a group which contains data from an external source (see
External data). Some groups, like
qftest
, env
and system
, are always defined
and have special meanings (see Special groups).
Again, depending on context, the text representation of the variable value may be returned, regardless of its type.
Referencing variables in scripts and script expressions
Accessing QF-Test variables in scripts and Script expressions (for example
$[Jython expression]
or the Condition of an
If node) is described in Variables.
- The run context methods
rc.get*
-
There are multiple methods in the run context module for accessing variables like
rc.get*
, such asrc.getStr
,rc.getInt
,rc.getNum
,rc.getBool
orrc.getObj
. A detailed description of all these methods is given in Run context API. - The run context property
rc.vars
-
The run context includes the
Map
-like objectrc.vars
for easy access to the current values of QF-Test variables. Designed as an alternative torc.getObj('name')
, it lets you writerc.vars.name
instead. When you use this expression to assign a value, it has the same effect of setting a local variable asrc.setLocal
. - The run context property
rc.groups
-
Similar to
rc.vars
, you can userc.groups
to access group variables: Instead ofrc.getObj('group','name')
orrc.getObj('qftest','dir.version')
you can userc.groups.group.name
orrc.groups.qftest.dir.version
. When you use this expression to assign a value, it has the same effect of setting a value in a group asrc.setGroupObject
. Note that elements in special groups may not allow write access. In that case, aReadOnlyPropertyException
is thrown. -
$(variable name)
and${group:name}
in Jython scripts -
In Jython scripts and script expressions, QF-Test variables can technically be referenced with the same syntax as in normal nodes. Since the Jython script is a string, the text value of the variable is directly embedded into the Jython code during expansion.
This way of referencing is not recommended. If the variable value contains items like backslashes (
\
) or line breaks, it can lead to unintended results.
Variable lookup
To understand the reasons of why and how variables are defined in multiple places, you first have to learn about how the values of variables are determined.
Each variable definition is placed on
one of two stacks of so-called bindings. One stack is used for direct definitions
and one for fallback bindings or default
values. When the value of a variable is requested, for example via
$(...)
, QF-Test first searches the stack of direct
bindings from top to bottom, then the stack of fallbacks, also top-down. The
first value found is used. If there is no binding at all for a
name, an UnboundVariableException
is thrown unless you use the special syntax
${default:varname:defaultvalue}
to
provide a default value for this case as described in Special groups.
|
|
|||||||
Primary stack (Direct bindings) |
Secondary stack (Default values) |
The mechanism supports recursive or self-referencing variable
definitions. For example, setting a variable named
classpath
to the value
some/path/archive.jar:$(classpath)
will extend a binding
for classpath
with lower precedence. If no such binding
exists, a RecursiveVariableException
is thrown.
Defining variables
Variables can be defined in various places.
- Variable definition tables
-
Two-column Tables are used, for example, in a Procedure call to define the parameter names and values to be passed, or in a Procedure node to set default values. In each row, one variable with a name and a value can be defined. In many other nodes, like Test suite, Test set and Test case, variables can be defined in tables, as well.
- Procedure return values
-
A Procedure can return a value. It will be assigned to the variable with the name given in the Procedure call node Variable for return value attribute. The called procedure can control the type of the returned object via the attribute Explicit object type of the Return node.
- Check results
-
One possible result handling in a check node is to assign the result to a variable named in the attribute Variable for result, for example in a Boolean check node.
- Return values of capture nodes
-
Capture nodes like Fetch text assign the received value to a variable named in the attribute Variable name.
- Set variable nodes
-
Variables can also be defined through Set variable nodes. The attribute Explicit object type sets the type of the returned object.
- Script nodes
-
Variables can be set in scripts via the methods
rc.setLocal
,rc.setGlobal
,rc.setLocalJson
etc. These can then be used in QF-Test nodes.setGroupObject
can be used to set variables in a variable group. For more information, see Scripting and Run context API. - Option dialog
-
Variables can be set and changed in the options dialog section "Variables" (see Variables). This is especially useful for global, system and command line variables.
Figure 6.2: Definition of system variables in the options dialog
Variable levels
Variables can be declared on different levels. There is the foundational difference in evaluation order of variable definitions between the primary and secondary stack. Both stacks then each have more fixed levels.
Primary stack
For the primary stack, the following order applies:
Local test case variables
Local test case variables are located in the upper part of the primary stack. When a node is entered during test execution, the defined variables (but not the fallback values!) are placed on top of the stack and removed again when leaving the stack.
For each node that has a variable definition table, a separate level is created on the primary stack, and the variables defined in the table placed there. Variables from local (not global!) assignments are added or updated in one of the existing levels, for example the return value of a procedure, the result variable of a check or capture node or variables created via Set variable or script nodes. The variable is added/updated in the level of the topmost procedure node, or if not available, of the test case node – if the variable does not already exist in a higher level (e.g. sequence, test step, loop, if node). In that case, the value of the variable is updated in that higher level.
To define local variables, the attribute Local variable must be enabled in the respective node.
This can be preconfigured in the QF-Test options (see Variables).
In scripts, local variables are set with the methods rc.setLocal
,
rc.setLocalJson
or rc.vars.name = value
(see Run context API).
Global variables
If the attribute Local variable is not active in nodes that can define variables,
these variables are created on the level of global variables.
In scripts, global variables are set with the methods rc.setGlobal
or rc.setGlobalJson
(see Run context API).
Note A global variable can also be created despite Local variable being set if no context is available for local variables. This is the case, for example, if a node is executed directly from the 'Extras' node.
A global variable remains unchanged until it is explicitly updated or cleared or QF-Test is quit. That means that global variables "survive" individual test runs. They serve to exchange values between independent test cases or procedures. Keep in mind that global variables must be defined by running the test before they can be referenced.
If you want to modify global variables without running a test, you can do this either through the debug mode (see Displaying variables in debug mode – Example) or the option dialog section "Variables".
To clear any global variables before a test run, use the menu entry
»Run«-»Clear global variables«. If QF-Test is running in batch mode (see Starting QF-Test)
global variables are cleared before running any test
passed through the command line argument -test <n>|<ID>
.
Command line argument variables
Command line argument variables can be set when launching QF-Test.
These are ranked above variables defined in the Test suite node.
On the command line the variables are set via the argument -variable <name>=<value>
,
see Command line arguments and exit codes.
Test suite node variables
Variables on this level of the stack are defined in the Test suite node of the current test suite. Typically, these variables are valid for all tests of the suite and can be overridden via the command line during a batch run if needed. A typical example is the choice of browser for running a web application that should differ between interactive test development and batch execution.
Seconday stack
The following order applies to the secondary stack:
Fallback values
When entering a node for which fallback values are defined, these are placed on top of the secondary stack. When a node from another test suite is called, the variables of the Test suite node of the original test suite are removed from the primary stack and placed on top of the secondary stack. When leaving the node (respectively the test suite), the variables are again removed from the secondary stack and, in the case of a test suite, moved to the primary stack into the respective level.
Entries are only placed on the secondary stack if fallback values were definied for a node or the originating test suite has variable definitions in its Test suite node.
System-specific variables
Here, path names and JDK- or OS-specific values or similar can be defined. This set of definitions is always located at the bottom of the secondary stack and therefore has the lowest binding priority.
System-specific variables are set in the option dialog section "Variables". They are stored in the system configuration file together with other system options.
Displaying variables in debug mode – Example
Consider the following example:

The Sequence "Login" contains a Procedure call of the
Procedure "login" which expects two parameters: user
and password
. The Parameter default values of the Procedure
are user=username
and password=pwd
. The
Procedure call overrides these with user=myname
and
password=mypassword
.
The "login" Procedure itself contains Procedure calls of other Procedures. Here, no parameters are passed. The procedures "setUser" and "setPassword" have one entry each in Parameter default values.
The following figure shows the overview of variable definitions when executing the procedure"setUser".

Let's take a closer look at the individual rows of the table:
- Procedure setUser: No variables defined.
- Procedure call setUser: No variables are passed because it is not required (different from e.g. Java). When checking the variable definitions, QF-Test goes through the table from top to bottom - regardless of procedure or test case borders. As soon as a variable with the matching name is found, the corresponding value is used.
- Procedure login: No variables defined.
- Procedure call login: Two variables are defined in this procedure call. The row was selected, so you can see the defined variables and their values on the right. At the current execution point of the test, the variable "name" will be used next. Since the first ocurrence of a variable with that name is in this row, the corresponding value "myName" will be used.
- Sequence Login: No variables defined.
- Test case Test: No variables defined.
- Global variables: The variable "client" was defined in the Dependency node, because it is needed in all test cases that interact with the application under test. Global variables remain unchanged until they are explicitly updated or cleared.
- Command line: Three variables were defined on the command line. One of them is the name of the browser that should be used for the current test run.
- Test suite: The name of the browser stored here would be used as a fallback if no other browser was defined in the rows above.
- Secondary stack: Signals the end of the primary stack and the beginning of the secondary stack below.
- Procedure setUser: A default value for the variable "name" is stored here. It would be used if no variable of that name existed in the rows above.
- Procedure login: Here default values for "name" and "password" are stored, as well. They would be used if no variables with those names existed in the rows above.
- System: No variables defined.
Data types of variables
With a few exceptions, all attribute fields in QF-Test nodes interpret entered values as plain text. Those exceptions are the conditions of If, Test case and Test set nodes, as well as script code attributes which expect valid expressions of a specific syntax.
Since the attributes are usually interpreted as text, a special syntax is needed to access variables or for calculations and string manipulations (see Variable references and Script expressions).
In script nodes, all data types that are available in their scripting language can be used independently of QF-Test. Inside the script interpreters, the data objects of any script can be used, see Variables. However, these will not show up in the variable stack of QF-Test and are not visible in the debug-mode variable definitions table or logged in the run log.
You can use the run context methods
rc.setLocal
and rc.setGlobal
to put a variable from a script onto the QF-Test variable stack.
This way, QF-Test variables can be assigned strings, but also values with other data types.
To set non-string values in a Set variable node you can use
Script expressions in the attribute Default value,
or you can enter the text representation of the value there
and set the desired object type in Explicit object type.
To access these variables, various methods are available. For QF-Test nodes, these are described in Variable references. For scripts and script expressions, methods are described in Variables, and special ones for Jython scripts in Jython Variables.
A detailed description of all run context methods can be found in Run context API.
JSON data
Data is often provided as JSON objects when working with HTTP requests or WebAPI.
If you want to serialize such an object, which means to convert it into a JSON string
and store it in a QF-Test variable, you can use the methods
rc.setLocalJson()
and rc.setGlobalJson()
of the run context
(see Run context API) in a script node.
If you want to convert a JSON string into a JSON object, you can use rc.getJson()
in a script node (see Run context API).
JSON objects can be modified and handled with the methods described in The JSON
module.
External data
You can access external data via Load properties, Excel data file,
Database, CSV data file and Load resources
nodes. These assign a set of definitions to a group name.
You can access the value of a resource or property via the description name
with the syntax ${group:name}
.
You can also access external data in a Data driver via Excel data file,
Database and CSV data file.
In that case however, no group is created.
Instead, a loop iteration is generated for each row of data, in which the values of the data set
are bound to QF-Test variables named according to the data column titles.
They can be accessed via the syntax $(column title)
.
When run in batch mode (see Starting QF-Test) QF-Test clears the resources and
properties before the execution of each test given with the -test <n>|<ID>
command line
argument. In interactive mode, QF-Test keeps them around to ease building a suite, but for a true
trial run you should clear them via the »Run«-»Clear resources and properties« menu first.
Special groups
The following variable groups are always available. Their values can be accessed via the syntax
$(group name:variable name)
,
or rc.groups.group.variable
in scripts.
- system
-
The group
system
gives access to the system properties of the Java VM (for programmers:java.lang.System.getProperties()
), e.g.${system:user.home}
for the user's home directory or${system:java.class.path}
for the class path with which QF-Test was started. Which names are defined in the groupsystem
depends on the utilised JDK.
The group always refers to the VM QF-Test was started with, because variable expansion takes place there. - env
-
On operating systems which support environment variables like
PATH
,TMP
orJAVA_HOME
(practically all systems QF-Test runs on), these environment variables can be accessed with the help of the groupenv
. - decrypt
-
9.0+
Via the
decrypt
group you can temporarily decrypt a string for the further usage in QF-Test, e.g. for text field inputs, API tokens or database passwords. In the run log, QF-Test will replace the expanded value by the placeholder***
. A value in a Set variable step can be encrypted by right-clicking and selecting »Encrypt text« from the popup menu.Note For specific values in QF-Test steps the run log always contains the final value. Please inspect the final run log before sharing it. Also pay attention to the remarks for the Salt for crypting passwords option.
- default
-
3.4+
You can specify a default value for a variable with the group
default
. The syntax is${default:varname:defaultvalue}
. This is extremely useful for things like generic components or in almost every place where there is a reasonable default for a variable because the default value is then tightly coupled with the use of the variable and doesn't have to be specified at Sequence or test suite level. Of course you should only use this syntax if the variable lookup in question is more or less unique. If you are using the same variable with the same default in different places it is preferable to use normal syntax and explicitly set the default, so that the default for all values can be changed in a single place. - as
-
9.0+
Like in a Set variable oder Return step it is possible to change
the typ of an object using the
as
group. The syntax is${as:type:value}
, whereas it is possible to reference values from variables invalue
using$(...)
. Valid values fortype
are:string
,str
,boolean
,number
,object
,pattern
,int
,integer
,long
,float
,double
,cmdline
, andjson
. - id
-
3.1+
The group
id
can be used to reference QF-Test component IDs. Values in this group simply expand to themselves, i.e. "${id:whatever}" expands to "whatever". Though QF-Test component IDs can be referenced without the help of this group, its use increases the readability of tests. Most notably however, QF-Test component ID references in this group will be updated automatically in case the referenced target component gets moved or its QF-Test ID changed. - idlocal
-
4.2.3+
The group
idlocal
is similar to theid
group but includes the path to the current test suite, i.e. "${idlocal:x}" expands to "path/to/current/suite/suite.qft#x". This enforces use of the component referenced in the suite that is current at the time of expansion, irrespective of whether there is a component with the same %attId; in the target suite of a procedure call. - quoteitem
-
4.0+
Via the
quoteitem
group you can conveniently escape special characters like '@', '&' and '%' in the name of a textual sub-item index to prevent it from being treated as several items, e.g. "${quoteitem:user@host.org}" will result in "user\@host.org". - quoteregex, quoteregexp
-
4.0+
The group
quoteregex
with its aliasquoteregexp
can be used to escape characters with special meaning in Regular expressions. This is often useful when building regular expressions dynamically or when referencing subitems with special characters in their name by a regular expression index, e.g. "componentid%${quoteregex:foo(baa)}.*" allows you to address the first occurrence of items beginning with 'foo(baa)'. - quotesmartid
-
6.0.1+
The
quotesmartid
group is similar toquoteitem
. In addition to the item syntax special characters '@', '&' and '%' it also escapes the characters ':', '=', '<' and '>' that have special meaning in SmartIDs, e.g. "${quotesmartid:Name: A & B}" will result in "Name\: A \& B". - qftest
-
The special group named
qftest
provides miscellaneous values that may be useful during a test run. The following tables list the values currently defined.Name Meaning 32
or32bit
No longer relevant because support for 32bit Java for QF-Test was dropped in version 8.0
true if QF-Test is running in a 32bit Java VM - which is not the same as running on a 32bit Operating System - false otherwise.64
or64bit
No longer relevant because support for 32bit Java for QF-Test was dropped in version 8.0
true if QF-Test is running in a 64bit Java VM, false otherwise.batch
true if QF-Test is running in batch mode, false for interactive mode. client.baseEngineName.<name>
The base name of the primary engine of the client started with the Client attribute set to <name>, e.g. fx
.client.browser.<name>
The name/type of the browser of the client started with the Client attribute set to <name>, e.g. safari
. Only available for Web clients.client.connectionMode.<name>
The name of the connection mode of the client started with the Client attribute set to <name>. Possible values are qfdriver
,cdpdriver
,webdriver
, andembedded
. Only available for Web clients.client.engine.<name>
The primary engine of the client started with the Client attribute set to <name>. The result consists of the base name of the engine and a numerical index, e.g. fx0
.client.engineNames.<name>
A list of all connected engines of the client started with the Client attribute set to <name>, e.g. [fx0, web_fx0]
.client.exitCode.<name>
The exit-code of the last process started with the Client attribute set to <name>. In case the process is still alive the result is the empty string. client.deviceName.<name>
A name for the (emulated) device started with the Client attribute set to <name>. Only available for Android clients after instrumentation, for emulated devices equal to the AVD name. client.deviceType.<name>
The type of the (emulated) device started with the Client attribute set to <name>. Can be emulator
for an emulation anddevice
for a connected real device. Only available for Android clients after instrumentation.client.mainVersion.<name>
The main version of the browser or device operating system of the client started with the Client attribute set to <name>, e.g. 121
. Only available for Web clients after first browser open and for Android clients after instrumentation.client.output.<name>
The output of the last process started with the Client attribute set to <name>. The maximum size for buffered output is defined by the option Maximum size of client terminal (kB). client.SDKVersion.<name>
The SDK version of the device operating system of the client started with the Client attribute set to <name>, e.g. 121
. Only available for Android clients after instrumentation.client.stdOut.<name>
The output on the standard output stream (stdout) of the last process (started with the Client attribute set to <name>). The maximum size for buffered output is defined by the option Maximum size of client terminal (kB). client.stdErr.<name>
The output on the standard error stream (stderr) of the last process (started with the Client attribute set to <name>). The maximum size for buffered output is defined by the option Maximum size of client terminal (kB). client.version.<name>
The version of the browser or device operating system of the client started with the Client attribute set to <name>, e.g. 121.10.2967.10
. Only available for Web clients after first browser open and for Android clients after instrumentation.clients
A list of the names of all active process clients, separated by a newline. clients.all
A list of the names of all process clients, separated by a newline. This includes live clients as well as the recent dead clients similar to those listed in the "Clients" menu. count.exceptions
Number of exceptions in the current test run. count.errors
Number of errors in the current test run. count.warnings
Number of warnings in the current test run. count.testCases
Total number of total test cases (run and skipped) in the current test run. count.testCases.exception
Number of test cases with exceptions in the current test run. count.testCases.error
Number of test cases with errors in the current test run. count.testCases.expectedToFail
Number of test cases expected to fail in the current test run. count.testCases.ok
Number of successful test cases in the current test run. count.testCases.ok.percentage
Percentage of successful test cases in the current test run. count.testCases.skipped
Number of skipped test cases in the current test run. count.testCases.notImplemented
Number of not implemented test cases in the current test run. count.testCases.run
Number of run test cases in the current test run. count.testSets.skipped
Number of skipped test sets in the current test run. dir.cache
Cache directory of QF-Test dir.groovy
Directory of Groovy dir.javascript
Directory of JavaScript dir.jython
Directory of Jython dir.log
Log directory of QF-Test dir.plugin
Plugin directory of QF-Test dir.root
Root directory of QF-Test dir.runlog
Run log directory of QF-Test dir.system
System-specific configuration directory of QF-Test. dir.user
User-specific configuration directory of QF-Test dir.version
Version-specific directory of QF-Test engine.<componentId>
Retrieves the GUI engine responsible for the given component (see GUI engines). language
The language in which QF-Test displays its graphical user interface. license
The path to the license file systemCfg
The path to the system configuration file userCfg
The path to the user specific configuration file executable
The qftest
executable matching the currently running QF-Test version, including the full path to itsbin
directory and with.exe
appended on Windows. Useful if you need to run QF-Test from QF-Test for example to call a daemon or create reports.isInRerun
"true", if current execution is in rerun mode, "false" otherwise, see Rerunning failing nodes immediately. isInRerunFromLog
"true", if test run has been re-started from run log, "false" otherwise, see Triggering rerun from a run log. java
Standard Java program ( javaw
under Windows,java
under Linux) or the explicit Java argument if QF-Test is started with-java <executable>
(deprecated)java.mainVersion
The major version of the JRE that QF-Test currently runs on, using 8 for Java 1.8 so the result is something like 8, 11 or 17. java.subVersion
The sub-version of the JRE that QF-Test currently runs on. For Java 8 the sub-version taken from after the '_', so for java.version 1.8.0_302 this results in 302. For Java 9 or higher this is the minor version, e.g. 9 in case of java.version 11.0.9. linux
"true" under Linux, "false" otherwise macOS
"true" under macOS, "false" otherwise os.fullVersion
The whole version of the operating system os.mainVersion
The main version of the operating system, e.g. "10" for Windows 10 os.name
The name of the operating system os.version
The version of the operating system. In some cases that's not the whole one then you should use os.fullversion
instead.project.dir
The directory to the current project. This variable is not defined in case the current test suite is not part of a project. rerunCounter
Number of current rerun attempt, default is 0, for details see Rerunning failing nodes immediately. return
The most recent value returned from a Procedure through a Return node. runID
The runid of the current test run. See Reports for further information about the runid. screen.height
Screen height in pixels screen.width
Screen width in pixels skipNode
This magic value is not for the casual user. It causes QF-Test to skip execution of the current node. Its primary use is as the value for a variable defined in the Text attribute of a Text input node which also has its Clear target component first attribute set. An empty value would clear the field whereas $_{qftest:skipnode} leaves the field unchanged. But skipnode is also applicable for fine-grained execution control by placing a variable in the comment of a node and selectively passing $_{qftest:skipnode} to that variable. Please note that you almost always want to use lazy syntax '$_' with this variable. Otherwise its expansion as the parameter in a Procedure call node would cause skipping the whole call. suite.dir
Directory of the current suite suite.file
File name of the current suite without directory suite.path
File name of the current suite including directory suite.name
Get the name of the current test suite. testCase.name
The name of the current Test case, empty if no Test case is currently being executed. testCase.id
The QF-Test ID of the current Test case, empty if no Test case is currently being executed. testCase.qName
The qualified name of the current Test case, including the names of its parent Test sets. Empty if no Test case is currently being executed. testCase.reportName
The expanded report name of the current Test case, empty if no Test case is currently being executed. testCase.splitLogName
The qualified name of the current Test case converted to a filename, including the names of its parent Test sets as directories. Empty if no Test case is currently being executed. testSet.name
The name of the current Test set, empty if no Test set is currently being executed. testSet.id
The QF-Test ID of the current Test set, empty if no Test set is currently being executed. testSet.qName
The qualified name of the current Test set, including the names of its parent Test sets. Empty if no Test set is currently being executed. testSet.reportName
The expanded report name of the current Test set, empty if no Test set is currently being executed. testSet.splitLogName
The qualified name of the current Test set converted to a filename, including the names of its parent Test sets as directories. Empty if no Test set is currently being executed. testStep.name
The name of the current Test step, empty if no Test step is currently being executed. testStep.qName
The qualified name of the current Test step, including the names of its parent Test steps, but not including Test cases or Test sets. Empty if no Test step is currently being executed. testStep.reportName
The expanded report name of the current Test step, empty if no Test step is currently being executed. thread
The index of the current thread. Always 0 except if QF-Test is started with the argument -threads <number>
.threads
The number of parallel threads. Always 1 except if QF-Test is started with the argument -threads <number>
.version
QF-Test version version.build
QF-Test build number windows
"true" under Windows, "false" otherwise Table 6.1: Definitions in the special group qftest
3.0+6.9
Immediate and lazy binding
There is a very subtle issue in using QF-Test variables that requires further explanation:
When a new set of variable bindings is pushed on one of the variable stacks, there are two possibilities for handling variable references in the value of a binding, for example when the variable named 'x' is bound to the value '$(y)'. The value '$(y)' can be stored literally, in which case it will be expanded some time in the future when '$(x)' is referenced somewhere, or it can be expanded immediately, so that the value of the variable 'y' is bound instead. The first approach is called 'lazy' or 'late binding', the second approach 'immediate binding'.
The difference, of course, is the time and thus the context in which a variable is expanded. In most cases there is no difference at all, but there are situations where it is essential to use either lazy or immediate binding. Consider the following two examples:
A utility test suite contains a procedure for starting the SUT with different JDK versions. The variable 'jdk' is passed as a parameter to this procedure. For ease of use, the author of the test suite defines some additional useful variables at test suite level, for example a variable for the java executable named 'javabin' with the value '/opt/java/$(jdk)/bin/java'. At the time 'javabin' is bound in the test suite variables, 'jdk' may be undefined, so immediate binding would cause an exception. But even if 'jdk' were bound to some value, immediate binding would not have the desired effect, because the java executable is supposed to be the one from the JDK defined later by passing the parameter 'jdk' to a procedure. Thus lazy binding is the method of choice here.
Imagine another utility test suite with a procedure to copy a file. Two parameters called 'source' and 'dest' specify the source file and destination directory. The caller of the procedure wants to copy a file called 'data.csv' from the same directory as the calling test suite to some other place. The natural idea is to bind the variable 'source' to the value '${qftest:suite.dir}/data.csv' in the procedure call. With immediate binding, '${qftest:suite.dir}' will indeed expand to the directory in which the calling suite resides. However, if lazy binding were used, the actual expansion would take place inside the procedure. In that case, '${qftest:suite.dir}' would expand to the directory of the utility suite, which most likely is not what the caller intended.
In versions of QF-Test up to and including 2.2 all variable expansion was lazy. As the examples above show, both variants are sometimes necessary. Since immediate binding is more intuitive it is now the default. This can be changed with the option When binding variables, expand values immediately. The option Fall back to lazy binding if immediate binding fails complements this and helps to ease migration of old test suites to the use of Immediate Binding. The warnings issued in this context help locating the few spots where you should use explicit lazy binding as described below. Except for very rare cases where lazy binding is required but immediate binding also works so that the fallback is not triggered, all tests should work out of the box.
In the few cases where it makes a difference whether a variable is expanded immediately or lazily, the expansion of choice can be selected individually, independent of the setting of the above option, by using an alternative variable syntax. For immediate binding use '$!' instead of just '$'. Lazy binding is selected with '$_'. For example, to define a variable at test suite level that specifies a file located in this test suite's directory, use '$!{qftest:suite.dir}/somefile'. If immediate binding is the default and you require lazy binding as in the 'jdk' example above, use '$_(jdk)'.
Note With lazy binding the order of variable or parameter definitions in a node or a data driver did not matter because nothing was expanded during the binding stage. With immediate bindings, variables are expanded top-to-bottom or, in a data driver, left-to-right. This means that if you define x=1 and y=$(x) it will work, with y being set to 1, if x is defined first. If y is defined first the definition will either fail or trigger the lazy definition fallback described above.