Expressions and variables
AutoTestX has a small, strict expression language. You use it in two ways.
| Field kind | Example field | How it reads |
|---|---|---|
| Template | Type → value, Open → url, Text → expected, HTTP → url/headers/body | Literal text. Only the parts inside {{ }} are expressions. Order {{poNumber}} for {{vendor}} |
| Expression | If → condition, While → condition, Expression → expression, Set variable → value, For each → items, Poll → until | The whole field is one expression. status == 'Approved' AND total > 0 |
The editor shows which kind a field is under the field.
In an expression field you may also write variables with braces:
{{status}} == 'Approved' means the same as status == 'Approved'.
Variables and where they come from
Section titled “Variables and where they come from”{{name}} is looked up in this order. The first match wins:
| Order | Source | Set by |
|---|---|---|
| 1 | Loop variables | For each … as line → {{line.item}}; Repeat … as i; component parameters |
| 2 | Runtime variables | Steps during the run: Extract, Set variable, HTTP saveAs, Custom code assignTo |
| 3 | Test variables | Test settings → Test variables (JSON) |
| 4 | Environment variables | The environment’s Variables (JSON) |
Two reserved prefixes are looked up first and never collide with variables:
| Prefix | Meaning |
|---|---|
{{secret.NAME}} |
A secret from the run’s environment. Masked everywhere. |
{{data.column}} |
The current row in a data-driven run. Reserved only when the test has a data source. |
Use dots for object fields and list positions: {{order.body.id}},
{{orders.body.0.id}}, {{line.item}}. Brackets work too: orders.body[0].
A missing variable is an error
Section titled “A missing variable is an error”{{custmer}} (a typo), or a variable whose setting step did not run, fails the step with
Unresolved reference “custmer”. Check the variable name and that an earlier step
assigned it. It never becomes an empty string. Silently typing nothing is how tests pass
while testing nothing.
Values and types
Section titled “Values and types”| Type | Literals |
|---|---|
| text | 'single' or "double" quotes, with \n, \t, \' escapes |
| number | 42, 3.14 |
| boolean | true, false |
| null | null |
| list | [1, 2, 3], ['a', 'b'] |
A template that is exactly one placeholder ({{qty}}) keeps the value’s type, e.g. a
number. Mixed text (Qty: {{qty}}) is always text.
Strict typing
Section titled “Strict typing”There is no automatic type conversion:
"5" == 5is an error (Cannot compare string with number. Convert explicitly, for example NUMBER(value).), notfalse.+is arithmetic only.1 + "1"is an error. Join text withCONCAT().- Only
trueandfalseare truthy.count AND …is an error; writecount > 0 AND ….
Values read from the page (Extract) are text. Convert with NUMBER() before numeric
comparisons.
Operators
Section titled “Operators”From lowest to highest precedence:
| Operator | Meaning |
|---|---|
OR, || |
Logical or (short-circuit) |
AND, && |
Logical and (short-circuit) |
NOT, ! |
Logical not |
==, != |
Strict equality (lists compare item by item) |
<, <=, >, >= |
Two numbers or two texts |
+, - |
Numbers only |
*, /, % |
Numbers only. Dividing by zero is an error. |
-x |
Negation |
( ) |
Grouping |
AND, OR, NOT are case-insensitive.
Functions
Section titled “Functions”Function names are case-insensitive. Functions cannot reach the page, network, files or secret store. For that, use a step.
| Function | Result | Example |
|---|---|---|
UPPER(t) / LOWER(t) / TRIM(t) |
Converted text | UPPER('po') → 'PO' |
LENGTH(t or list) |
Number of characters or items | LENGTH('PO-1') → 4 |
SUBSTRING(t, start[, length]) |
Part of the text (0-based) | SUBSTRING('PO-1234', 3) → '1234' |
REPLACE(t, find, with) |
Every occurrence replaced | REPLACE('a-b-c', '-', '/') → 'a/b/c' |
CONCAT(a, b, …) |
Joined text | CONCAT('Vendor ', NOW()) |
SPLIT(t, separator) |
List | SPLIT('a,b', ',') → ['a','b'] |
JOIN(list, separator) |
Text | JOIN(['a','b'], ', ') → 'a, b' |
CONTAINS(t or list, x) |
true/false | CONTAINS(status, 'Approv') |
STARTS_WITH(t, prefix) / ENDS_WITH(t, suffix) |
true/false | STARTS_WITH(poNumber, 'PO-') |
PAD_START(t, length[, fill]) |
Padded text (default fill: space) | PAD_START('42', 5, '0') → '00042' |
REGEX_MATCH(t, pattern) |
true/false | REGEX_MATCH(poNumber, '^PO-\\d+$') |
REGEX_EXTRACT(t, pattern[, group]) |
Matched text or null |
REGEX_EXTRACT('Order PO-77 saved', 'PO-(\\d+)', 1) → '77' |
Backslashes in expression text. Inside quotes in an expression,
\starts an escape. The only valid escapes are\n(newline),\t(tab),\\(a backslash),\'and\". A regex therefore needs doubled backslashes: type'^PO-\\d+$'. Any other escape, such as a single\d, is a syntax error that gives its position and suggests\\d. You see it when you save the step. It is never quietly changed into a different pattern. Character classes avoid the issue:'^PO-[0-9]+$'.This applies only to expressions. In a template field, such as Text → expected with match
regex, type the pattern normally:^PO-\d+$.
Numbers
Section titled “Numbers”| Function | Result |
|---|---|
NUMBER(x) |
Text → number (error if not a number) |
ROUND(n[, decimals]) |
Rounded number |
ABS(n) |
Absolute value |
MIN(a, b, …) / MAX(a, b, …) |
Smallest / largest |
| Function | Result |
|---|---|
COUNT(list) |
Number of items |
FIRST(list) / LAST(list) |
First / last item, or null if empty |
Dates are ISO-8601 text in UTC, e.g. 2026-09-30T21:59:31.000Z.
| Function | Result |
|---|---|
NOW() |
The current date and time |
DATE_ADD(date, amount, unit) |
Date moved by amount ms, seconds, minutes, hours, days or weeks (negative to go back) |
FORMAT_DATE(date, pattern) |
Text using yyyy, MM, dd, HH, mm, ss (UTC) |
FORMAT_DATE(DATE_ADD(NOW(), 7, 'days'), 'dd/MM/yyyy') → '07/10/2026'Examples
Section titled “Examples”| Goal | Field | Write |
|---|---|---|
| Unique name per run | Set variable → value | CONCAT('Vendor ', NOW()) |
| Check a generated number | Expression | STARTS_WITH(poNumber, 'PO-') AND LENGTH(poNumber) > 3 |
| Compare an extracted amount | Expression | NUMBER(total) == 49.5 |
| Branch on API status | If → condition | response.status == 404 |
| Loop over test data | For each → items | lines |
| Date in a field | Type → value | {{FORMAT_DATE(NOW(), 'yyyy-MM-dd')}} |
| Poll until done | Poll → until | response.body.state == 'done' |