Skip to main content
New to Testkube? Unleash the power of cloud native testing in Kubernetes with Testkube. Get Started >

Test Workflows - Expressions

Expressions Language

We have designed a simple expressions language, that allows dynamic evaluation of different values.

JSON-Native

It is built on JSON, so every JSON syntax is a valid expression value as well, like [ "a", "b", "c" ].

Math

You can do basic math easily, like config.workers * 5.

Defining a Condition:

condition: 'config.workers > 1 || config.force'

Dynamic Image Tag:

image: 'mcr.microsoft.com/playwright:v{{ config.version }}'

Configurable K6 Script:

content:
files:
- path: /k6-script.js
content: |
export const options = {
thresholds: {{ json(config.thresholds) }}
};
{{ config.script }}

Operators

Arithmetic

The operators have the precedence defined so the order will follow math rules. Examples:

  • 1 + 2 * 3 will result in 7
  • (1 + 2) * 3 will result in 9
  • 2 * 3 ** 2 will result in 18
OperatorReturnsDescriptionExample
== (or =)boolIs equal?3 == 5 is false
!= (or <>)boolIs not equal?3 != 5 is true
>boolIs greater than?3 > 5 is false
<boolIs lower than?3 < 5 is true
>=boolIs greater than or equal?3 >= 5 is false
<=boolIs lower than or equal?3 <= 5 is true
&&the last value or the falsy oneAre both truthy?true && false is false
5 && 0 && 3 is 0
5 && 3 && 2 is 2
||first truthy value or the last valueIs any truthy?true || false is true
5 || 3 || 0 is 5
0 || 5 is 5
"" || "foo" is "foo"
!boolIs the value falsy?!0 is true
? and :any of the values insideTernary operator - if/elsetrue ? 5 : 3 is 5
+string or floatAdd numbers together or concatenate text1 + 3 is 4
"foo" + "bar" is "foobar"
"foo" + 5 is "foo5"
-floatSubtract one number from another5 - 3 is 2
%floatDivides numbers and returns the remainder5 % 3 is 2
/floatDivides two numbers6 / 3 is 2
10 / 4 is 2.5
Edge case: 10 / 0 is 0 (for simplicity)
*floatMultiplies one number by the other4 * 2 is 8
**floatExponentiation - power one number to the other2 ** 5 is 32
( and )the inner typeCompute the expression altogether(2 + 3) * 5 is 20

Access

OperatorDescriptionExample
.Access inner value{"id": 10}.id is 10
["a", "b"].1 is "b"
.*.Wildcard mapping[{"id": 5}, {"id": 3}].*.id is [5, 3]
...Spread arguments operatorshellquote(["foo", "bar baz"]...) is equivalent of shellquote("foo", "bar baz")

Built-in Variables

General Variables

There are some built-in variables available. Part of them may be resolved before execution (and therefore used for Pod settings), while the others may be accessible only dynamically in the container.

Selected variables

NameResolved immediatelyDescription
alwaysAlias for true
neverAlias for false
config variables (like config.abc)Values provided for the configuration
execution.idTestWorkflow Execution's ID
execution.nameTestWorkflow Execution's name
execution.numberTestWorkflow Execution's sequence number
execution.scheduledAtTestWorkflow Execution's scheduled at date
resource.idEither execution ID, or unique ID for parallel steps and services
resource.rootEither execution ID, or nested resource ID, of the resource that has scheduled it
namespaceNamespace where the execution will be scheduled
workflow.nameName of the executed TestWorkflow
organization.idOrganization ID (when running with Control Plane)
environment.idEnvironment ID (when running with Control Plane)
dashboard.urlURL of the environment's Dashboard
labels variables (like labels.some_label_key)Labels of the executed Test Workflow (., -, / are replaced by _)
env variables (like env.SOME_VARIABLE)Environment variable value
failedIs the TestWorkflow Execution failed already at this point?
passedIs the TestWorkflow Execution still not failed at this point?
services (like services.db.0.ip or services.db.*.ip)Get the IPs of initialized services

Contextual Variables

In some contexts, there are additional variables available.

Retry Conditions

When using custom retry condition, you can use self.passed and self.failed for determining the status based on the step status.

spec:
steps:
- shell: exit 0
# ensure that the step won't fail for 5 executions
retry:
count: 5
until: 'self.failed'

Matrix and Shard

When using services (service pods), parallel (parallel workers), or execute (test suite) steps:

  • You can use matrix.<name> and shard.<name> to access parameters for each copy.
  • You can access index and count that will differ for each copy.
  • Also, you may use matrixIndex, matrixCount, shardIndex and shardCount to get specific indexes/numbers for combinations and shards.
spec:
services:
# Start two workers and label them with index information
db:
count: 2
description: "Instance {{ index + 1 }} of {{ count }}" # "Instance 1 of 2" and "Instance 2 of 2"
image: mongo:latest
# Run 2 servers with different node versions
api:
matrix:
node: [20, 21]
description: "Node v{{ matrix.node }}" # "Node v20" and "Node v21"
image: "node:{{ matrix.node }}"

Built-in Functions

Casting

There are some functions that help to cast values to a different type. Additionally, when using wrong types in different places, the engine tries to cast them automatically.

NameReturnsDescriptionExample
stringstringCast value to a stringstring(5) is "5"
string([10, 15, 20]) is "10,15,20"
string({ "foo": "bar" }) is "{\"foo\":\"bar\"}"
listlist of provided valuesBuild a list of valueslist(10, 20) is [ 10, 20 ]
intintMaps to integerint(10.5) is 10
int("300.50") is 300
boolboolMaps value to booleanbool("") is false
bool("1239") is true
floatfloatMaps value to decimalfloat("300.50") is 300.5
evalanythingEvaluates the expressioneval("4 * 5") is 20

General

NameReturnsDescriptionExample
joinstringJoin list elementsjoin(["a", "b"]) is "a,b"
join(["a", "b"], " - ") is "a - b"
splitlistSplit string to listsplit("a,b,c") is ["a", "b", "c"]
split("a - b - c", " - ") is ["a", "b", "c"]
trimstringTrim whitespaces from the stringtrim(" \nabc d ") is "abc d"
lenintLength of array, map or stringlen([ "a", "b" ]) is 2
len("foobar") is 6
len({ "foo": "bar" }) is 1
floorintRound value downfloor(10.5) is 10
ceilintRound value upceil(10.5) is 11
roundintRound value to nearest integerround(10.5) is 11
atanythingGet value of the elementat([10, 2], 1) is 2
at({"foo": "bar"}, "foo") is "bar"
tojsonstringSerialize value to JSONtojson({ "foo": "bar" }) is "{\"foo\":\"bar\"}"
jsonanythingParse the JSONjson("{\"foo\":\"bar\"}") is { "foo": "bar" }
toyamlstringSerialize value to YAMLtoyaml({ "foo": "bar" }) is "foo: bar\n
yamlanythingParse the YAMLyaml("foo: bar") is { "foo": "bar" }
shellquotestringSanitize arguments for shellshellquote("foo bar") is "\"foo bar\""
shellquote("foo", "bar baz") is "foo \"bar baz\""
shellparse[]stringParse shell argumentsshellparse("foo bar") is ["foo", "bar"]
shellparse("foo \"bar baz\"") is ["foo", "bar baz"]
maplist or mapMap list or map values with expression; _.value and _.index/_.key are availablemap([1,2,3,4,5], "_.value * 2") is [2,4,6,8,10]
filterlistFilter list values with expression; _.value and _.index are availablefilter([1,2,3,4,5], "_.value > 2") is [3,4,5]
jqanythingExecute jq against valuejq([1,2,3,4,5], ". | max") is [5]
range[]intBuild range of numbersrange(5, 10) is [5, 6, 7, 8, 9]
range(5) is [0, 1, 2, 3, 4]
relpathstringBuild relative pathrelpath("/a/b/c") may be ./b/c
relpath("/a/b/c", "/a/b") is "./c"
abspathstringBuild absolute pathabspath("/a/b/c") is /a/b/c
abspath("b/c") may be /some/working/dir/b/c
chunk[]listSplit list to chunks of specified maximum sizechunk([1,2,3,4,5], 2) is [[1,2], [3,4], [5]]
datestringReturn current date (either 2006-01-02T15:04:05.000Z07:00 format or custom argument (Go syntax)date() may be "2024-06-04T11:59:32.308Z"
date("2006-01-02") may be 2024-06-04
secretstringCreates an environment variable with a reference to the secret. First parameter is a secret name, second parameter is a key namesecret("name", "key") is "env.S_N_name_K_key"
secret("name", "key") is "env.S_N_name_K_key"

File System

These functions are only executed during the execution.

NameReturnsDescriptionExample
filestringFile contentsfile("/etc/some/path") may be "some\ncontent"
glob[]stringFind files by patternglob("/etc/**/*", "./x/**/*.js") may be ["/etc/some/file", "/etc/other/file", "/some/working/dir/x/file.js"]
condition: 'len(glob("/data/repo/**/*.spec.js")) > 0'
- shell: 'generate-api-key.sh > /data/api-key'
- execute:
workflows:
- name: example
config:
entries: '{{ tojson(split(file("/data/list*), "\n")) }}'
script: '{{ file("/data/api-key") }}'
shell: |
/bin {{ shellquote("--", config.args ...) }}