Why Does a TestStand Case Step Ignore Multiple Values?

Mark Townsend6 min read
Other ManufacturerOther TopicTroubleshooting
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

You have a barcode-driven dispatcher in TestStand 4.1.1. A Select step with one value per Case (Case 1, Case 2, Case 3, Case 4) routes every product correctly. Collapse the products that share a test into Case 1,2 and Case 3,4, and no Case fires for any product. The sequence keeps growing because every new barcode needs its own Case block.

Skip the fixes that waste your time

These are the moves engineers usually make first. None of them fixes the grouped Case.

  • One Case per barcode, duplicated body. It works, but every shared test now lives in two or more places. The sequence doubles in size and the copies drift apart the first time someone edits only one of them.
  • Reformatting the value list. Adding spaces, wrapping values in braces, or quoting the list as "1,2" does not turn the Case into a multi-value match. The quoted version becomes a single literal string that no scanned barcode will ever equal.
  • Nesting a second Select inside a Case. Adds depth and still needs one Case per value at the inner level.
  • Cloning the whole test sequence per product. Same maintenance problem as duplicated Case bodies, only bigger.

Rule these out and move on. The problem is how the expression engine reads the comma.

Understand what the comma does inside a Case expression

A Case step does not take a value list. It takes one expression, evaluates it, and compares the result against the Select expression. In the TestStand expression language the comma is an operator, not a list separator: it evaluates each sub-expression left to right and returns the value of the last one. So 1,2 evaluates to 2, and 3,4 evaluates to 4.

That gives you at best a Case that matches only the last value in the list. If nothing matches at all, a second mismatch sits on top: the data type of the Select expression.

What you see Cause
Grouped Case fires only for the last value in the list Comma operator returns the last sub-expression; earlier values are discarded
Grouped Case never fires; single-value Cases work Select expression is a string from the scanner, Case evaluates to a number (or the reverse), so the comparison never returns True
Nothing fires, even single-value Cases Scanner appends a terminator or padding, so the string never equals the expected code
Default Case always runs Any of the above; the Default branch hides the mismatch

Check the Select expression type before touching the Cases

Do this first. It takes two minutes and tells you which fix you need.

  1. Set a breakpoint on the Select step and run one product through the barcode read.
  2. Open the Watch View and add the variable feeding the Select. Read its type (Number or String) and its exact contents.
  3. Look for trailing characters. Many scanners append a carriage return or line feed, and some pad fixed-length fields. A string code with a hidden terminator will never equal a clean literal.
  4. Evaluate a single Case expression in the watch or expression browser and compare its type to the Select result. A String compared against a Number is a non-match every time.

If the codes are numeric, convert the scanned string once, right after the read, for example with Val() into a Number local. Do the conversion in one place, not in every Case.

Rebuild the dispatch so one branch handles several barcodes

Two layouts work reliably. Pick one and use it everywhere in the station.

Option A: If / ElseIf with OR conditions

This is the layout most TestStand developers recommend for this exact job. Each branch takes a Boolean expression, so a group of barcodes is just an OR chain.

  1. Replace the Select step with an If step.
  2. Write the first group as one Boolean expression.
  3. Add an Else If for each additional group and a final Else that fails the UUT with an "unknown product" message.
If      Locals.ProductCode == 1 || Locals.ProductCode == 2
        Sequence Call -> Test_Family_A
Else If Locals.ProductCode == 3 || Locals.ProductCode == 4
        Sequence Call -> Test_Family_B
Else
        Fail: unknown product code
End

Locals.ProductCode is an example name; use your own variable. For string codes, compare against quoted literals: Locals.Barcode == "1001" || Locals.Barcode == "1002".

Option B: Select on True

If you want to keep the Select/Case visual layout, put a constant in the Select and the Booleans in the Cases.

  1. Set the Select expression to True.
  2. Set each Case expression to a Boolean OR chain, for example Locals.ProductCode == 1 || Locals.ProductCode == 2.
  3. Keep a Default Case that flags an unknown code instead of silently passing.

Each Case now evaluates to True or False, and the first one that equals the Select value of True runs. Keep the groups mutually exclusive; if a code appears in two Cases, only the first one ever fires.

Option C: Move the mapping into data

When the product list keeps growing, stop encoding it in step structure. Store a table of barcode-to-test-family pairs (a station global, a file loaded at startup, or a container array), look up the family name after the scan, and use a single Sequence Call that takes its sequence name from an expression. Adding a product becomes a data edit, not a sequence edit.

Prove every barcode lands in the right test

  1. Build a checklist of every barcode currently in production with its expected test family.
  2. Run each one, or feed the code manually into the variable at the breakpoint, and single-step through the dispatch. Confirm the expected branch executes.
  3. Feed one invalid code and confirm the Else/Default branch fails the UUT. A dispatcher that passes an unknown product is worse than one that crashes.
  4. Scan with the real scanner at least once per branch to catch terminator or padding characters that manual entry hides.
  5. Check the report: the executed sequence name should appear for every product. Keep the checklist and rerun it whenever you add a barcode.

Avoid the traps that bring the mismatch back

  • Single = in a condition. In a TestStand expression, = assigns. Locals.ProductCode = 1 overwrites the code and evaluates as the assigned value. Use ==.
  • Mixed types across branches. One branch compares against 1001, another against "1002". Convert once and compare one type everywhere.
  • Leading zeros. Converting "0012" to a number gives 12. If the zeros are meaningful, compare as strings.
  • Overlapping groups. A code listed in two branches runs only the first. Keep each code in exactly one place, which Option C enforces by design.
  • No Default branch. Without it, an unmatched code skips the whole dispatch and the UUT can report a pass with no tests run.

FAQ

What happens if I write Case 1,2 in a TestStand Select step?

The comma acts as an operator and the expression evaluates to the last value, 2. At best only that value matches; if the Select expression is a string, nothing matches.

What happens if the barcode is a string and my Case values are numbers?

The comparison never returns True, so the Case is skipped and the Default branch (if any) runs. Convert the scanned string once with Val() or compare against quoted string literals.

What happens if two Case expressions both evaluate to True with Select True?

Only the first matching Case runs; the later one is ignored. Keep each barcode in exactly one group.

How do I match several barcodes in one TestStand branch?

Use an If or Else If with an OR chain such as Locals.ProductCode == 1 || Locals.ProductCode == 2, or set the Select expression to True and put that OR chain in the Case.

What happens if the scanner adds a carriage return to the code?

String comparisons fail because the stored value contains the extra character. Check the variable in the Watch View, then strip the terminator in the scanner configuration or right after the read.

If a Select True or If/ElseIf dispatch still skips a branch after you have confirmed matching types and clean scanner data at a breakpoint, stop editing the sequence. Export a minimal sequence that reproduces the mismatch and take it to NI technical support with your exact TestStand version.

Back to blog