Why write tests, and what should I test first?

CS Fundamentals 2 min read
Short answer

Tests are what let you refactor confidently. Start with pure functions holding real business logic, then add a test for every bug you fix so it cannot come back.

python
def test_factorial_base_cases():
    assert factorial(0) == 1
    assert factorial(1) == 1

def test_factorial_rejects_negatives():
    with pytest.raises(ValueError):
        factorial(-1)

#What to test first

  1. Pure functions with real logic — calculations, parsing, validation. Easy to test, high value.
  2. Bugs you have fixed. Write the failing test first, then fix it. That bug can never silently return.
  3. Edge cases: empty input, one item, the maximum, zero, negative, null.

#What not to bother with

  • Getters and setters with no logic.
  • Third-party libraries — they have their own tests.
  • Exact HTML output, which changes constantly for cosmetic reasons.

#Test behaviour, not implementation

python
# Brittle — breaks when you rename a private helper
assert calculator._internal_state == 5

# Robust — survives any refactor that keeps the behaviour
assert calculator.add(2, 3) == 5

If your tests break every time you refactor without changing behaviour, they are testing the wrong thing.

#Arrange, act, assert

python
def test_average_ignores_empty():
    scores = []                    # arrange
    result = average(scores)       # act
    assert result == 0             # assert

One behaviour per test, and a name that says what it checks. When it fails at 2am, the name should be enough.

#Coverage is a floor, not a goal

100% coverage with assertions that check nothing proves nothing. A well-chosen 60% that covers your real logic is worth more than 95% padded with getter tests.

#The real payoff

It is not catching bugs today. It is that six months from now you can restructure a module and know within seconds whether you broke anything. Without tests, every change is a gamble, so nobody changes anything, and the code rots.