← back to section

Every test comes down to one thing: compare what you expected with what you got. Everything else is preparation around that comparison. That makes comparisons and conditions the part of the language a tester needs most.

A comparison gives "yes" or "no"

live example

code = 200
print(code == 200)     # True
print(code == 404)     # False
Run

Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Free week →

Two equals signs == ask "are these equal?", one sign = stores a value. Mixing them up is easy, and the mistake is quiet, so say it out loud: one stores, two ask.

Comparison operators:

a == b     # equal
a != b     # not equal
a > b      # greater
a < b      # less
a >= b     # greater or equal
a <= b     # less or equal

The result is always True or False. Python writes them capitalised — it does not know true.

A condition: do one thing or the other

live example

code = 404

if code == 200:
    print("All good")
else:
    print("Something went wrong")
Run

Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Free week →

The indent here is part of the language. What belongs to the if is shifted four spaces to the right. Most languages use curly braces for this; Python uses the indent. Get it wrong and the program either fails with IndentationError or does the wrong thing.

More than two branches — then elif in the middle:

if code == 200:
    print("Success")
elif code == 404:
    print("Not found")
elif code == 500:
    print("Server error")
else:
    print(f"Unexpected code: {code}")

Checks run top to bottom, the first matching one wins, the rest are skipped. The final else catches everything else — and it is not a formality: a test that silently ignores an unexpected response is worse than one that shouts about it.

Several conditions at once

if code == 200 and total > 0:
    print("Order created and not empty")

if code == 404 or code == 410:
    print("Order is gone")

if not found:
    print("Nothing found")
  • and — true when both are true;
  • or — when at least one is true;
  • not — flips the value.

What counts as empty

A condition can hold a value, not just a comparison. Empty things count as false:

if response:          # true when the string is not empty
    ...
if not items:         # true when the list is empty
    ...

False values: 0, the empty string "", the empty list [], the empty dict {} and None — the special "no value at all". Everything else is true.

None deserves its own note: it is not zero and not an empty string. In service responses it appears where a field was never filled, and it is compared with a special operator:

if paid_at is None:
    print("Order is not paid")

is None, not == None — that is the convention and it is safer.

How this looks in a check

live example

expected = 200
actual = 404

if actual == expected:
    print("OK")
else:
    print(f"FAIL: expected {expected}, got {actual}")
Run

Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Free week →

That is already a real check, only written by hand. In a real test you write assert instead of if, and the language builds the failure message for you — see assert and errors.

What to solve

In short

  • = stores, == asks; the result of a comparison is True or False.
  • if / elif / else picks a branch; the first match wins and else catches the rest — which is what keeps an unexpected response from passing silently.
  • A four-space indent is part of the language, not formatting.
  • and, or, not combine conditions; empty string, empty list, 0 and None count as false.
  • "No value" is None, and you check it with is None.