Skip to content

Lesson 1 of 9 · 18m

Tests That Earn Their Keep

What to test, what to skip, and why a test you don't trust is worse than no test at all.

Test suites rot. The code gets refactored, the tests get skipped because "they're flaky," and six months later nobody knows which failures are real. The test that fails for the wrong reason is worse than no test — it trains everyone to ignore red. The discipline is choosing what to test so the suite stays green and meaningful.

The three questions a test must answer

  1. What behavior is being verified? — "the rate limiter blocks after 5 attempts", not "function returns true".
  2. What breaks if this fails? — a real user-facing behavior, not an implementation detail.
  3. Why did it fail? — a good test name says the expected behavior, so a failure reads like a bug report.

If you cannot answer all three for a test, delete it — it is noise wearing a test costume.

Test behavior, not implementation

# Bad: tests the implementation
def test_calls_validate():
    assert validate.called_with(payload)

# Good: tests the behavior callers depend on
def test_rejects_missing_username():
    assert signup({"email": "[email protected]"}) == {"error": "username required"}

The first test breaks every time you rename a helper. The second only breaks when signup's actual contract changes — which is the only failure worth alerting on.

Try it yourself — the contract, in JS

// The behavior test asserts the OUTPUT, not the internal calls:
function rateLimit(n, limit = 5) { return n > limit; }
console.log("over limit:", rateLimit(6));      // should be true
console.log("under limit:", rateLimit(3));     // should be false
// If this fails, the LIMITER is wrong — not the test.

The ones that pay for themselves

Bug fixes (the test proves the bug), public API contracts, tricky edge cases, and anything that has ever broken in production. Skip trivial getters, framework plumbing, and anything tested by your type checker already.