Learning Goals
3 min- Distinguish static vs dynamic testing.
- Distinguish black-box vs white-box testing.
- Define smoke, regression, sanity, and acceptance tests.
- Tell positive (happy-path) from negative (error-path) tests.
Warm-Up · A Map of the Words
5 minWHEN do you test? static (without running) vs dynamic (by running) HOW MUCH do you see? black-box (just inputs/outputs) vs white-box (the code) WHY this run? smoke / sanity / regression / acceptance WHAT path? positive (valid input) vs negative (invalid input)
These aren't competing methods — they're dimensions. A single test is a point in this space: e.g., "a dynamic, white-box, negative regression test". Knowing the words lets you describe exactly what you're checking.
New Concept · The Core Terms
14 minStatic vs dynamic
static examine code WITHOUT running it
→ code review, linting (ruff), type checking (mypy)
dynamic examine code BY running it
→ unit tests, integration tests, manual testingBoth matter. Static analysis (Lessons 41-44) catches whole classes of bugs before the code ever runs.
Black-box vs white-box
black-box test only inputs → outputs; ignore the internal code
"given 10 and 20, the average should be 15"
white-box test with knowledge of the internals
"make sure BOTH branches of this if-statement run"Positive vs negative tests
# positive (happy path): valid input → correct output assert divide(10, 2) == 5 # negative (error path): invalid input → correct error try: divide(10, 0) assert False, "should have raised" except ZeroDivisionError: pass # good — it raised as expected
Beginners only write positive tests. Professionals write negative ones too — most real bugs hide on the error paths.
Test purposes
smoke test quick "does it even start?" check before deeper testing sanity test quick check that a specific fix works regression re-run old tests to ensure new changes broke nothing acceptance does it meet the user's/business's requirements?
A few more you'll meet
unit test tests one small piece (a function/class) in isolation integration tests pieces working together test fixture the setup data/state a test needs test double a stand-in for a real thing (mock, stub, fake) flaky test a test that sometimes passes, sometimes fails — fix it!
Worked Example · Label the Tests
12 minFor a login function, here are four tests. Label each on every dimension:
Test A: "correct username + password → logged in"
→ dynamic, black-box, positive, (acceptance-ish)
Test B: "wrong password → 'invalid credentials' error"
→ dynamic, black-box, negative
Test C: "the password-hashing branch runs for new users"
→ dynamic, white-box (we know the internal branch)
Test D: mypy flags that password is typed str, not bytes
→ static (no code is run)# the same login, tested both ways def login(user, pwd, db): record = db.get(user) if record is None: raise ValueError("unknown user") if record["pwd"] != pwd: raise ValueError("invalid credentials") return "logged in" db = {"aisyah": {"pwd": "secret"}} # positive assert login("aisyah", "secret", db) == "logged in" # negative (two error paths) for bad in [("aisyah", "wrong"), ("ghost", "x")]: try: login(*bad, db) assert False, "should have raised" except ValueError: pass print("all login checks passed ✅")
Read the diff
One small function, four kinds of test. The positive test proves it works; the two negative tests prove it fails correctly; a static check (mypy) would catch type mistakes without running anything. Good test suites cover all of these — not just the happy path.
Try It Yourself
13 minClassify each: a code review, running unit tests, mypy type checking, a manual click-through, ruff linting.
Answer
Static: code review, mypy, ruff. Dynamic: running unit tests, manual click-through.
For a function celsius_to_f(c), write one positive and one negative test.
Hint
assert celsius_to_f(100) == 212 # positive try: celsius_to_f("hot") # negative: bad type assert False except TypeError: pass
Make a one-page table of all 12+ terms from this lesson, each with a one-sentence definition and a tiny example. You'll reference it for the PCET exam.
Mini-Challenge · Categorise a Suite
8 minImagine a banking app. Write down 8 tests you'd want, then label each across all four dimensions (static/dynamic, black/white-box, positive/negative, and its purpose). Aim for variety — at least 2 negative and 1 static.
Show one possible solution
1. deposit RM100 → balance up 100 dynamic/black/positive/acceptance 2. withdraw more than balance → error dynamic/black/negative 3. withdraw negative amount → error dynamic/black/negative 4. app launches without crashing dynamic/black/positive/smoke 5. mypy: amounts are Decimal not float static 6. transfer triggers both debit+credit dynamic/white 7. old "interest" test still passes dynamic/regression 8. statement matches bank requirements dynamic/acceptance
Non-negotiables: variety across dimensions, at least two negative tests, at least one static check.
Recap
3 minTesting terms are dimensions, not rivals. Static (no run) vs dynamic (run). Black-box (outputs only) vs white-box (knows the code). Positive (valid input) vs negative (invalid input). Purposes: smoke, sanity, regression, acceptance. Speak these fluently — the PCET exam and every QA conversation use them. Next: the levels of testing.
Vocabulary Card
- static / dynamic
- Testing without running code / by running it.
- black-box / white-box
- Testing from outputs only / with knowledge of the internals.
- positive / negative
- Happy-path (valid input) tests / error-path (invalid input) tests.
- regression test
- Re-running old tests to ensure new changes didn't break anything.
Homework
4 minPick an app you use daily (a messaging app, a game, a banking app). Write 6 tests in plain English and label each on all four dimensions. Include at least 2 negative tests and 1 static check.
Follow the mini-challenge format with your chosen app. The label per test is the point — it proves you can place any test in the vocabulary space.