Live demo — data resets daily at 03:00 UTC. Nothing you enter is saved. Server UI →

product: assert audience: test-developer authority: normative

Measurement Patterns

Numeric measurement (default type)

Pass condition: value >= low_limit && value <= high_limit.

measurement:
  name: "VOUT_5V"              # REQUIRED — stored in every record
  target: 5.05                 # mock: used as measured value
  value: "{{measured_v}}"      # gRPC runners: template resolves to actual
  low_limit: 4.75              # lower bound (inclusive)
  high_limit: 5.25             # upper bound (inclusive)
  unit: "V"

Numeric comparison operators

operator Aliases Evaluation
(omitted) value >= low_limit && value <= high_limit
equal eq value == target
notequal ne value != target
greaterthan gt value > low_limit
greaterthanorequal gte, ge value >= low_limit
lessthan lt value < high_limit
lessthanorequal lte, le value <= high_limit
log Always PASS; informational only

These operators apply to type: numeric only. For type: boolean and type: string the only evaluation mechanism IS the expected: field — however, operator: log IS supported on any type (numeric, boolean, string) to make the measurement informational-only.

Boolean measurement

Pass condition: value == expected.

Operators are NOT supported for type: boolean. Omitting expected: causes the measurement to always PASS (informational-only).

measurement:
  name: "RELAY_SELFTEST"
  type: boolean
  value: "true"
  expected: "true"

String measurement

Pass condition: value == expected (exact, case-sensitive).

Operators are NOT supported for type: string. Omitting expected: causes the measurement to always PASS.

measurement:
  name: "FW_VERSION"
  type: string
  value: "v2.1.0"
  expected: "v2.1.0"

Informational-only measurement (log operator)

operator: log applies to all measurement types. The measurement always evaluates to PASS — the value IS recorded without any limit or expected-value check.

# Numeric — record a current draw without limits
measurement:
  name: "SUPPLY_CURRENT_LOG"
  target: 0.245
  unit: "A"
  operator: log

# String — record a MAC address
measurement:
  name: "MAC_ADDRESS"
  type: string
  operator: log
  value: "{{mac_address}}"

# Boolean — record a pin state
measurement:
  name: "BOOT_FLAG"
  type: boolean
  operator: log
  value: "{{boot_flag}}"

Multiple measurements from one step

Use measurements: (plural) when a single function returns several values:

- name: "Read all power rails"
  runner: python
  runner_type: python3.11
  module: "instruments"
  function: "read_power_rails"
  outputs:
    rail_3v3: "{{v3v3}}"
    rail_5v0: "{{v5v0}}"
  measurements:
    - name: "RAIL_3V3"
      value: "{{v3v3}}"
      low_limit: 3.135
      high_limit: 3.465
      unit: "V"
    - name: "RAIL_5V0"
      value: "{{v5v0}}"
      low_limit: 4.75
      high_limit: 5.25
      unit: "V"

measurements: takes precedence over measurement: when both are present.

Grouping measurements (group)

Every measurement accepts an optional group label. It is purely organisational — it never affects the verdict — and lets you tag measurements so reports can group and filter them instead of showing one long flat list. The classic case is repeated, identical hardware: a DUT with five identical connectors can tag each connector's measurements with CON1..CON5.

measurements:
  - name: "CONT_PIN1"
    value: "{{con1_pin1}}"
    low_limit: 0
    high_limit: 1
    unit: "Ohm"
    group: "CON1"          # ← organisational tag; shown as a badge in the report
  - name: "CONT_PIN2"
    value: "{{con1_pin2}}"
    low_limit: 0
    high_limit: 1
    unit: "Ohm"
    group: "CON1"

group supports {{variable}} templates, so a sub-sequence reused per connector can set group: "CON{{connector_index}}" and each invocation lands in its own group.

Notes:

  • group is part of the measurement's structural identity: two measurements that are otherwise identical but carry different groups are distinct definitions (so their statistics/yield are tracked separately).
  • Max length 100 characters. Omit it entirely for ungrouped measurements.
  • Grouping is complementary to structuring the test into one step (or sub-sequence) per connector, which already gives each connector its own collapsible section and verdict. Use per-step structure for independent verdicts/yield; use group when you want to tag measurements that live within a shared step.

Declaring a "no result" value (invalid_value)

Many packages seed their numeric variables to a sentinel such as -999, so that a step which errors or never runs FAILS honestly instead of recording a plausible midpoint. That is the right thing to do for the verdict, and it quietly ruins the statistics: nothing else in the system distinguishes this is not a reading from this is a reading, so the sentinel is averaged in and Cp/Cpk end up describing a process that nobody runs.

invalid_value declares it. Numeric measurements only.

variables:
  vout: "-999"             # seeded so an unrun step cannot pass

measurements:
  - name: "VOUT_5V"
    value: "{{vout}}"
    low_limit: 4.75
    high_limit: 5.25
    unit: "V"
    invalid_value: -999    # ← "no result", not a measurement

What it changes, and what it deliberately does not:

Verdict Unchanged. -999 is still outside the limits, so the step still FAILS.
Report Unchanged. The operator still sees the value that was recorded.
Yield Unchanged. The failure still counts against yield.
Statistics The sample is excluded from mean, sigma, Cp and Cpk, and counted in excludedInvalidSamples.

The exclusion is applied at query time as well as at record time, so declaring invalid_value on an existing measurement also cleans up the history that was recorded before you declared it.

It is part of the structural specification, like low_limit and group, so adding or changing it creates a new definition version.

An unhandled error has occurred. Reload

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please retry or reload the page.