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

Test Workflows – Expressions

Our Test Workflows system uses a simple, yet powerful, expressions language to dynamically evaluate and transform values at runtime. This language is built on JSON and supports basic arithmetic, logical operations, and many useful built-in functions, enabling you to customize your workflows flexibly.

Overview​

  • JSON-Native:
    Since the language is based on JSON, every valid JSON value is also a valid expression. For example, [ "a", "b", "c" ] is a valid expression.

  • Mathematical Operations:
    You can perform arithmetic calculations directly within expressions. For instance, you can multiply a configuration parameter by a number like so:

    config.workers * 5
  • Dynamic Evaluation:
    Expressions are used to evaluate conditions, dynamically set image tags, generate scripts, and more. They allow you to manipulate values on the fly during workflow execution.

Examples​

Defining a Condition​

You can define conditions using logical and arithmetic operators. For example, this condition checks if there is more than one worker or if a force flag is set:

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

Dynamic Image Tag​

By embedding expressions within double curly brackets {{ }}, you can dynamically generate values. In the example below, the container image tag is determined by the configuration parameter version:

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

Generating Array values​

By surrounding embedded expressions within brackets [ ], you can dynamically generate array property values. In the example below, the git.content paths are taken from a comma-separate config value:

 content:
git:
uri: https://github.com/kubeshop/testkube
revision: main
paths: ["{{ split(config.paths) }}"]

Configurable K6 Script​

Expressions can also be used to build dynamic content files. For example, you can create a K6 script that uses JSON-parsed thresholds and additional script content from your configuration:

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

Operators​

The expressions language supports a variety of operators. They follow standard mathematical rules for precedence.

Arithmetic Operators​

  • Addition (+) and Subtraction (-):
    Perform addition or subtraction on numbers. They also allow string concatenation.

    Examples:

    • 1 + 2 * 3 evaluates to 7
    • (1 + 2) * 3 evaluates to 9
    • "foo" + "bar" evaluates to "foobar"
  • Multiplication (*), Division (/), and Modulo (%):
    Multiply, divide, or find the remainder between numbers.

    Examples:

    • 4 * 2 evaluates to 8
    • 10 / 4 evaluates to 2.5
    • 5 % 3 evaluates to 2
  • **Exponentiation (**):**
    Raise a number to the power of another.

    Example:

    • 2 ** 5 evaluates to 32
  • Parentheses (( and )):
    Group expressions to control the order of evaluation.

    Example:

    • (2 + 3) * 5 evaluates to 20

Comparison and Logical Operators​

Operator Returns Description Example
== or = bool Checks equality 3 == 5 is false
!= or <> bool Checks inequality 3 != 5 is true
> bool Greater than 3 > 5 is false
< bool Less than 3 < 5 is true
>= bool Greater than or equal to 3 >= 5 is false
<= bool Less than or equal to 3 <= 5 is true
&& bool Logical AND (returns the last truthy or first falsy value) true && false is false
|| bool Logical OR (returns the first truthy or last value) 0 || 5 is 5
! bool Logical NOT !0 is true
? : any Ternary operator (if/else) true ? 5 : 3 is 5

Access Operators​

These operators allow you to access elements within data structures:

  • .: Access a property or element
    Example: {"id": 10}.id returns 10

  • .*.: Wildcard mapping
    Example: [{"id": 5}, {"id": 3}].*.id returns [5, 3]

  • ...: Spread arguments
    Example: shellquote(["foo", "bar baz"]...) is equivalent to shellquote("foo", "bar baz")

Built-in Variables​

The expressions language provides several built-in variables. Some of these are resolved before execution (and can be used in Pod settings), while others are available only dynamically within the container.

General Variables​

Name Resolved Immediately Description
always ✅ Alias for true
never ✅ Alias for false
config (e.g., config.abc) ✅ User-provided configuration values
execution.id ✅ ID of the current TestWorkflow execution
execution.name ✅ Name of the current TestWorkflow execution
execution.number ✅ Sequence number of the execution
execution.tags.<name> ✅ Tags passed to the execution with --tag
execution.scheduledAt ✅ Scheduled execution date/time
resource.id ✅ Unique ID for parallel steps or services
resource.root ✅ Resource ID of the parent resource
namespace ✅ Namespace where the execution runs
workflow.name ✅ Name of the executed TestWorkflow
organization.id ✅ Organization ID (when using Control Plane)
environment.id ✅ Environment ID (when using Control Plane)
dashboard.url ✅ URL of the environment’s Dashboard
labels (e.g., labels.some_label_key) ✅ Labels assigned to the TestWorkflow (special characters are normalized)
env (e.g., env.SOME_VARIABLE) ❌ Environment variable values (resolved at runtime)
failed ❌ Indicates if the execution has already failed
passed ❌ Indicates if the execution has not yet failed
services (e.g., services.db.0.ip) ❌ IP addresses of initialized services

Contextual Variables​

Additional variables are available in specific contexts:

Retry Conditions​

When defining a custom retry condition, you can use self.passed and self.failed to determine the step status:

spec:
steps:
- shell: exit 0
# Retry until the step has failed, for up to 5 executions
retry:
count: 5
until: "self.failed"

Matrix and Shard​

When running steps in parallel (via services, parallel, or execute steps), you can access:

  • matrix.<name> and shard.<name> for custom parameters
  • index and count for each copy
  • matrixIndex, matrixCount, shardIndex, and shardCount for detailed indexing

Example using matrix values:

spec:
services:
# Deploy two workers with index information
db:
count: 2
description: "Instance {{ index + 1 }} of {{ count }}" # "Instance 1 of 2" and "Instance 2 of 2"
image: mongo:latest
# Deploy two servers with different Node.js versions
api:
matrix:
node: [20, 21]
description: "Node v{{ matrix.node }}" # "Node v20" and "Node v21"
image: "node:{{ matrix.node }}"

Built-in Functions​

The expressions language includes many functions that help manipulate and cast values.

Casting Functions​

Function Returns Description Example
string string Casts a value to a string string(5) returns "5"
list list Creates a list from provided values list(10, 20) returns [10, 20]
int int Converts a value to an integer int(10.5) returns 10
bool bool Converts a value to a boolean bool("") returns false
float float Converts a value to a floating-point number float("300.50") returns 300.5
eval any Evaluates an expression eval("4 * 5") returns 20

General Functions​

Function Returns Description Example
join string Joins list elements into a single string join(["a", "b"]) returns "a,b"; join(["a", "b"], " - ") returns "a - b"
split list Splits a string into a list based on a delimiter split("a,b,c") returns ["a", "b", "c"]
trim string Removes whitespace from both ends of a string trim(" \nabc d ") returns "abc d"
len int Returns the length of an array, map, or string len(["a", "b"]) returns 2
floor int Rounds a number down to the nearest integer floor(10.5) returns 10
ceil int Rounds a number up to the nearest integer ceil(10.5) returns 11
round int Rounds a number to the nearest integer round(10.5) returns 11
at any Retrieves an element from a list or map by index/key at([10, 2], 1) returns 2; at({"foo": "bar"}, "foo") returns "bar"
tojson string Serializes a value to JSON format tojson({ "foo": "bar" }) returns "{\"foo\":\"bar\"}"
json any Parses a JSON string into an object json("{\"foo\":\"bar\"}") returns { "foo": "bar" }
toyaml string Serializes a value to YAML format toyaml({ "foo": "bar" }) returns a YAML string
yaml any Parses a YAML string into an object yaml("foo: bar") returns { "foo": "bar" }
shellquote string Sanitizes and quotes arguments for safe shell usage shellquote("foo bar") returns "\"foo bar\""
shellparse list Parses a shell command string into an array of arguments shellparse("foo bar") returns ["foo", "bar"]
map list or map Applies an expression to each element in a list or map map([1,2,3,4,5], "_.value * 2") returns [2,4,6,8,10]
filter list Filters a list using an expression predicate filter([1,2,3,4,5], "_.value > 2") returns [3,4,5]
jq any Executes a jq expression on a value jq([1,2,3,4,5], ". | max") returns 5
range list Generates a range of numbers range(5, 10) returns [5, 6, 7, 8, 9]; range(5) returns [0, 1, 2, 3, 4]
relpath string Computes a relative file path relpath("/a/b/c") might return ./b/c
abspath string Computes an absolute file path abspath("b/c") might return /some/working/dir/b/c
chunk list Splits a list into chunks of a specified maximum size chunk([1,2,3,4,5], 2) returns [[1,2], [3,4], [5]]
date string Returns the current date in a specified format date() might return "2024-06-04T11:59:32.308Z"; date("2006-01-02") might return "2024-06-04"
secret string Creates a reference to a secret as an environment variable secret("name", "key") returns a reference like "env.S_N_name_K_key"

File System Functions​

Function Returns Description Example
file string Returns the contents of a file file("/etc/some/path") might return "some\ncontent"
glob list Finds files matching a pattern glob("/etc/**/*", "./x/**/*.js") might return ["/etc/some/file", "/etc/other/file", "/some/working/dir/x/file.js"]

Example using file and glob functions:

condition: 'len(glob("/data/repo/**/*.spec.js")) > 0'

Another example combining file reading and command generation:

- 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") }}'

You can also use functions like shellquote to safely pass arguments to shell commands:

shell: |
/bin {{ shellquote("--", config.args ...) }}

By leveraging the Test Workflows expressions language, you can build dynamic, configurable workflows that perform complex evaluations and manipulations at runtime. For additional details on operators, built-in functions, and contextual variables, please refer back to this document whenever needed.