Test Workflows - Matrix and Sharding
This Workflows functionality is not available when running the Testkube Agent in Standalone Mode - Read More
Often you want to run a test with multiple scenarios or environments, either to distribute the load or to verify it on different setup.
Test Workflows have a built-in mechanism for all these cases - both static and dynamic.
Usage
Matrix and sharding features are supported in Services (services), and both Test Suite (execute) and Parallel Steps (parallel) operations.
- Services (
services) - Test Suite (
execute) - Parallel Steps (
parallel) 
kind: TestWorkflow
apiVersion: testworkflows.testkube.io/v1
metadata:
  name: example-matrix-services
spec:
  services:
    remote:
      matrix:
        browser:
        - driver: chrome
          image: selenium/standalone-chrome:4.21.0-20240517
        - driver: edge
          image: selenium/standalone-edge:4.21.0-20240517
        - driver: firefox
          image: selenium/standalone-firefox:4.21.0-20240517
      image: "{{ matrix.browser.image }}"
      description: "{{ matrix.browser.driver }}"
      readinessProbe:
        httpGet:
          path: /wd/hub/status
          port: 4444
        periodSeconds: 1
  steps:
  - shell: 'echo {{ shellquote(join(map(services.remote, "tojson(_.value)"), "\n")) }}'
kind: TestWorkflow
apiVersion: testworkflows.testkube.io/v1
metadata:
  name: example-matrix-test-suite
spec:
  steps:
  - execute:
      workflows:
      - name: k6-workflow-smoke
        matrix:
          target:
          - https://testkube.io
          - https://docs.testkube.io
        config:
          target: "{{ matrix.target }}"
apiVersion: testworkflows.testkube.io/v1
kind: TestWorkflow
metadata:
  name: example-sharded-playwright
spec:
  content:
    git:
      uri: https://github.com/kubeshop/testkube
      paths:
      - test/playwright/playwright-project
  container:
    image: mcr.microsoft.com/playwright:v1.32.3-focal
    workingDir: /data/repo/test/playwright/playwright-project
  steps:
  - name: Install dependencies
    shell: 'npm ci'
  - name: Run tests
    parallel:
      count: 2
      transfer:
      - from: /data/repo
      shell: 'npx playwright test --shard {{ index + 1 }}/{{ count }}'
Syntax
This feature allows you to provide few properties:
matrixto run the operation for different combinationscount/maxCountto replicate or distribute the operationshardsto provide the dataset to distribute among replicas
Both matrix and shards can be used together - all the sharding (shards + count/maxCount) will be replicated for each matrix combination.
Matrix
Matrix allows you to run the operation for multiple combinations. The values for each instance are accessible by matrix.<key>.
In example:
parallel:
  matrix:
    image: ['node:20', 'node:21', 'node:22']
    memory: ['1Gi', '2Gi']
  container:
    resources:
      requests:
        memory: '{{ matrix.memory }}'
  run:
    image: '{{ matrix.image }}'
Will instantiate 6 copies:
index | matrixIndex | matrix.image | matrix.memory | shardIndex | 
|---|---|---|---|---|
0 | 0 | "node:20" | "1Gi" | 0 | 
1 | 1 | "node:20" | "2Gi" | 0 | 
2 | 2 | "node:21" | "1Gi" | 0 | 
3 | 3 | "node:21" | "2Gi" | 0 | 
4 | 4 | "node:22" | "1Gi" | 0 | 
5 | 5 | "node:22" | "2Gi" | 0 | 
The matrix properties can be a static list of values, like:
matrix:
  browser: [ 'chrome', 'firefox', '{{ config.another }}' ]
or could be dynamic one, using Test Workflow's expressions:
matrix:
  files: 'glob("/data/repo/**/*.test.js")'
Sharding
Often you may want to distribute the load, to speed up the execution. To do so, you can use shards and count/maxCount properties.
shardsis a map of data to split across different instancescount/maxCountare describing the number of instances to startcountdefines static number of instances (always)maxCountdefines maximum number of instances (will be lower if there is not enough data inshardsto split)
- Replicas (
countonly) - Static sharding (
count+shards) - Dynamic sharding (
maxCount+shards) 
parallel:
  count: 5
  description: "{{ index + 1 }} instance of {{ count }}"
  run:
    image: grafana/k6:latest
__
parallel:
  count: 2
  description: "{{ index + 1 }} instance of {{ count }}"
  shards:
    url: ["https://testkube.io", "https://docs.testkube.io", "https://app.testkube.io"]
  run:
    # shard.url for 1st instance == ["https://testkube.io", "https://docs.testkube.io"]
    # shard.url for 2nd instance == ["https://app.testkube.io"]
    shell: 'echo {{ shellquote(join(shard.url, "\n")) }}'
parallel:
  maxCount: 5
  shards:
    # when there will be less than 5 tests found - it will be 1 instance per 1 test
    # when there will be more than 5 tests found - they will be distributed similarly to static sharding
    testFiles: 'glob("cypress/e2e/**/*.js")'
  description: '{{ join(map(shard.testFiles, "relpath(_.value, \"cypress/e2e\")"), ", ") }}'
Similarly to matrix, the shards may contain a static list, or Test Workflow's expression.
Counters
Besides having the matrix.<key> and shard.<key> there are some counter variables available in Test Workflow's expressions:
indexandcount- counters for total instancesmatrixIndexandmatrixCount- counters for the combinationsshardIndexandshardCount- counters for the shards
Matrix and sharding together
Sharding can be run along with matrix. In that case, for every matrix combination, we do have selected replicas/sharding. In example:
matrix:
  browser: ["chrome", "firefox"]
  memory: ["1Gi", "2Gi"]
count: 2
shards:
  url: ["https://testkube.io", "https://docs.testkube.io", "https://app.testkube.io"]
Will start 8 instances:
index | matrixIndex | matrix.browser | matrix.memory | shardIndex | shard.url | 
|---|---|---|---|---|---|
0 | 0 | "chrome" | "1Gi" | 0 | ["https://testkube.io", "https://docs.testkube.io"] | 
1 | 0 | "chrome" | "1Gi" | 1 | ["https://app.testkube.io"] | 
2 | 1 | "chrome" | "2Gi" | 0 | ["https://testkube.io", "https://docs.testkube.io"] | 
3 | 1 | "chrome" | "2Gi" | 1 | ["https://app.testkube.io"] | 
4 | 2 | "firefox" | "1Gi" | 0 | ["https://testkube.io", "https://docs.testkube.io"] | 
5 | 2 | "firefox" | "1Gi" | 1 | ["https://app.testkube.io"] | 
6 | 3 | "firefox" | "2Gi" | 0 | ["https://testkube.io", "https://docs.testkube.io"] | 
7 | 3 | "firefox" | "2Gi" | 1 | ["https://app.testkube.io"] |