What a test is
You already know how to write Python functions, since you did it all through Python for Beginners. A test is just more Python: a small piece of code that runs your function and checks the answer automatically.
The checking tool is assert, a built-in statement you may remember from Advanced Python. It works like this:
assert conditiondoes nothing if the condition isTrue- if the condition is
False, it raises anAssertionErrorand the program stops
That is the whole trick. Instead of running your code, staring at the output, and judging it by eye, you write the judgment down as code. The computer then re-checks it every time, in milliseconds, forever.
This habit is not optional in real jobs. Professional teams run thousands of these automated checks on every change, and at most companies code that fails them is blocked from shipping.
Without tests, every change to a shared codebase is a gamble that you remembered to re-check everything by hand. Nobody remembers, which is why untested codebases rot into the sort of file everyone is afraid to touch. This course teaches you to write those checks, and in its second half, to debug with a method when they go red.
Three facts about one function
Each assert checks one fact about add. All three facts are true, so nothing stops the program and the final print runs.
def add(a, b): return a + b assert add(2, 3) == 5 assert add(-1, 1) == 0 assert add(0, 0) == 0 print("all 3 tests passed")
Output
all 3 tests passedThe silence is the point. A passing assert produces no output at all, so the only thing you see is the final print, and that absence of noise is what lets hundreds of tests run without burying you in output.
Notice which three cases were chosen. Two positive numbers is the ordinary case, a negative plus a positive checks that signs work, and two zeros checks the smallest possible input. That spread is deliberate, and unit 3 turns choosing cases into a method rather than a guess.
The final print is a crude but real test report. It only runs if every assert above it passed, so seeing it is a genuine signal, and lesson 1-3 builds a proper runner on the same idea.
When a test fails
Suppose add above were changed to return a - b. The first assert becomes assert -1 == 5, which is false, so Python raises AssertionError and the print never happens.
That crash is the test doing its job. A failing test is not an annoyance, it is information, telling you that one exact fact about your code is no longer true. You found the bug the moment you created it, at your desk, instead of a user finding it next week.
Only the first failure is reported, which is worth knowing early. Python stops at the first false assert, so the second and third are never evaluated, and a broken function can hide several problems behind one message.
Two words you will use constantly:
- a test passes, or is green, when its assertions are all true
- a test fails, or is red, when an assertion is false or the code crashes
What a false assert does
assert x == 10 when x is 7 raises an AssertionError and stops the program immediately.
assert is all-or-nothing. A true condition is completely silent, and a false one raises right away, with no return value and no way for the surrounding code to shrug it off:
x = 7 assert x == 10 # AssertionError, execution stops here print("never runs")
That hard stop is what makes tests trustworthy, because a red test can never be quietly ignored by the program. A checking function that merely returned False would be easy to call and forget to look at.
The bare message is the one real weakness of assert, since AssertionError on its own does not say what the value actually was. Lesson 2-3 shows how to attach a message that reports the offending value, which turns a failure from a puzzle into an answer.
Testing a function that returns a bool
Three asserts covering both answers is_even can give, plus the boundary value 0.
def is_even(n): return n % 2 == 0 assert is_even(4) == True assert is_even(7) == False assert is_even(0) == True print("all 3 tests passed")
Output
all 3 tests passedThe first assert could also be written as assert is_even(4) with no comparison, since the function already returns a True or False value. Both forms are correct, and the explicit == True is arguably clearer in a test because it states the expected answer out loud.
The false case does need its comparison, or something equivalent. assert is_even(7) == False states the expectation directly, and assert not is_even(7) says the same thing. What would be wrong is assert is_even(7), which asserts the opposite of the truth and would fail.
Zero earns its own line because it is the value most likely to be handled wrongly. A naive implementation using n % 2 == 1 for oddness would still call 0 even, but plenty of hand-rolled versions do not, and testing the boundary is how you find out.
The same pattern on a branching function
absolute_value has two branches, and these three asserts exercise both of them plus the value that divides them.
def absolute_value(n): if n < 0: return -n return n assert absolute_value(-5) == 5 assert absolute_value(3) == 3 assert absolute_value(0) == 0 print("all 3 tests passed")
Output
all 3 tests passedEach line is one fact, and reading them top to bottom describes what the function is for. That readability is a real benefit of tests beyond catching bugs, since a stranger can learn the function's contract from its asserts faster than from its body.
Zero is the boundary between the two branches, which is exactly why it earns its own assert. The condition is n < 0, so 0 takes the second path, and a version written with n <= 0 would return -0 instead. In Python that happens to equal 0 and the test would still pass, but the same off-by-one in a language without that quirk, or in a function returning something other than a number, would break.
Note that no assert here checks a non-integer input like -2.5. Nothing in the code stops it from working, and nothing tests that it does, which is the sort of gap unit 3 teaches you to notice on purpose.