Great work!

XP to next level

BugEater

Operator Precedence Traps

Learning Objectives

By the end of this lesson you will be able to:

  • Explain operator precedence and its effect on boolean expressions
  • Identify the two most common precedence-related specification mismatches
  • Design test cases that distinguish between A || (B && C) and (A || B) && C

What Is Operator Precedence?

In mathematics, multiplication has higher precedence than addition: 2 + 3 × 4 = 2 + 12 = 14, not 5 × 4 = 20.

In most programming languages, && (AND) has higher precedence than || (OR). This means:

A || B && C

is actually parsed as:

A || (B && C)

Not (A || B) && C. These are different logical expressions. If a developer writes the first intending the second, the behavior differs — and the bug only manifests in specific input combinations.

The Two Interpretations

Specification: "Free shipping applies if the user is a VIP member, OR if the order total exceeds €150 AND it includes 3 or more items."

Interpretation 1 (what the developer intended): isVIP || (total > 150 && itemCount >= 3) → VIPs always get free shipping; non-VIPs need both conditions

Interpretation 2 (what a misread could produce): (isVIP || total > 150) && itemCount >= 3 → Even VIPs need 3+ items

These give the same result in many cases. They differ exactly when:

  • isVIP=true, total=50, itemCount=1 → Interpretation 1: free shipping. Interpretation 2: paid shipping.

The Test Cases That Catch Precedence Bugs

For any A || B && C expression, test:

  • A=true, B=false, C=false → Both interpretations agree: true (A wins)
  • A=false, B=true, C=true → Both agree: true (B&&C wins)
  • A=true, B=true, C=false → Interpretation 1: true (A wins). Interpretation 2: false (C fails)
  • A=false, B=true, C=false → Interpretation 1: false. Interpretation 2: false. Both agree.

The critical test: A=true and C=false. If the developer wrote (A || B) && C, a VIP member without enough items gets charged for shipping.

The Parenthesis Problem

The root cause is rarely malice or carelessness — it's the invisible parentheses that programming languages add silently. A developer writing a complex condition doesn't always see them.

The tester's advantage: you don't need to read the code. You need to design inputs that distinguish between the two readings of the specification, then observe which behavior the system actually produces.

Pro Tip: Whenever you see a compound condition mixing && and || without explicit parentheses in the spec, write two interpretations and design one test case that distinguishes them. That case is worth more than five happy-path tests.

Short-Circuit Evaluation: A Related Trap

In most languages, A || B stops evaluating as soon as A is true. This means if A is always true in your tests, you never exercise B at all. Make sure at least some test cases have A=false to force evaluation of B.

Similarly for A && B: test with A=false to verify B is never evaluated (and that the system handles A=false gracefully without relying on B being non-null).

Summary

Operator precedence bugs are specification-level ambiguities realized as code. The developer read the requirement one way; the language parsed their code another. Your job is to design the test that distinguishes between the two interpretations — and to do it without reading a single line of source code.

Quiz

In most programming languages, which operator has higher precedence than OR?

A tester wants to verify the precedence trap in A || B && C. Which set of inputs BEST distinguishes the two interpretations?

A condition reads: if (!isExpired && isActive || isAdmin). Which group gains unexpected access?

What is the safest practice to avoid precedence traps?