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:
groupis 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
groupwhen 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.