beginner24 min

Testing Fundamentals

Learn why automated tests matter and write simple assert-based tests for your own functions.

What you'll learn

  • Explain why automated tests catch problems manual checking misses
  • Write simple test functions using Python's assert statement
  • Describe what frameworks like unittest and pytest add beyond plain assert statements

Prerequisites

Explanation

Every exercise in this course so far has been checked by a program, automatically, the moment you ran it — that's testing, and it's exactly the same idea professional developers rely on to keep large codebases trustworthy as they grow.

Why manual checking isn't enough. Running a function once and eyeballing its output feels sufficient in the moment, but it doesn't scale: the moment you change anything elsewhere in a program, you'd have to remember every place that might now behave differently and re-check each one by hand. Automated tests do that re-checking for you, instantly, every single time — catching a regression (something that used to work but now doesn't) the moment it's introduced, rather than after it ships.

assert. Python's built-in assert statement is the simplest possible test: assert condition does nothing at all if condition is True, and raises an AssertionError immediately if it's False. Wrapping a few assertions in an ordinary function turns them into a reusable, repeatable test:

def test_add():
    assert add(2, 3) == 5

Calling test_add() either completes silently (the test passed) or raises an error pointing at the exact failing line (the test failed) — no separate "checking" step required.

Testing the unhappy path. Good tests don't just confirm the expected, common case works — they also confirm your code behaves sensibly on edge cases: an empty list, a zero, a negative number, or input that should be rejected outright. If a function is supposed to raise an exception for bad input, you test that by calling it inside a try/except and failing the test (assert False) if the expected exception never showed up — proving the guard actually works, not just that the normal case does.

Where unittest and pytest come in. Plain assert functions work, but as a project grows to hundreds of tests, you want more: automatically discovering and running every test file without listing them by hand, a clear pass/fail report instead of a stack trace, shared setup/teardown logic that runs before and after each test, and the ability to run just one failing test in isolation. Python's built-in unittest module and the very popular third-party pytest package both provide exactly that — they are built around the same assert idea you just used, adding structure and tooling on top rather than replacing the core concept.

The mindset, more than the tool. The valuable habit isn't memorizing a testing framework's API — it's the instinct to ask "how would I know if this broke?" for every function you write, and to write that check down as code instead of a mental note you'll forget to make next week.

Example

Two simple assert-based tests for an add() function, run directly.

def add(a, b):
    return a + b


def test_add_positive_numbers():
    assert add(2, 3) == 5


def test_add_negative_numbers():
    assert add(-1, -1) == -2


test_add_positive_numbers()
test_add_negative_numbers()
print("All tests passed!")

Try it yourself

Change one assert to an incorrect expected value and press Run to see what an AssertionError looks like.

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Guided exercise

Guided exercise

Complete test_is_palindrome with two assert statements checking is_palindrome('Racecar') is True and is_palindrome('Hello') is False.

Checks: is_palindrome('Level') is True · is_palindrome('Python') is False · plus 2 hidden checks

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Stuck? Get a hint.

Independent exercise

Independent exercise

Write average(numbers) (raising ValueError for an empty list) plus three assert-based test functions: a typical case, a single-element case, and one confirming the empty-list case raises ValueError.

Checks: average([2, 4, 6]) == 4.0 · average([10]) == 10.0 · average([]) raises ValueError · plus 2 hidden checks

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Stuck? Get a hint.

Common mistakes

  • Only testing the happy path and never checking edge cases like empty input, zero, or values that should be rejected.
  • Writing tests that depend on leftover state from a previous test, so they only pass when run in a specific order.
  • Treating 'the code ran without crashing' as proof it's correct, instead of asserting the actual expected result.
  • Forgetting to actually call a test function after defining it — a test that's never run can never fail, but it also never catches anything.

Knowledge check

Knowledge check

1. What happens when assert condition runs and condition is False?
2. What is a 'regression' in the context of testing?
3. What do frameworks like unittest and pytest add on top of plain assert statements?
4. How can you test that calling a function with invalid input correctly raises an exception, using plain assert-based testing?

Takeaway

Automated tests are just code that checks other code: assert catches problems the moment they're introduced, instead of leaving you to notice them by chance later.

Summary

Automated tests replace manual, easy-to-forget re-checking with code that verifies behavior every time, catching regressions immediately. Python's assert statement is the simplest building block — it raises an AssertionError on a False condition — and frameworks like unittest and pytest add discovery, reporting, and shared setup on top of that same core idea. Good tests deliberately cover edge cases and expected failures, not just the common case.

References

Your notes

Notes save automatically.

Finished this lesson?

Mark it complete to track your progress and schedule a future review.