Goal

You’ll be able to write basic tests using assert statements, and understand why writing tests genuinely changes how confidently you can modify existing code later, not just catch bugs upfront.

Learn

An assert statement checks that a condition is True, raising an AssertionError if it’s not:

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

assert add(2, 3) == 5   # passes silently, no output
assert add(2, 3) == 6   # raises AssertionError!

This is the core building block of testing: write code that calls your function with known inputs, and assert that the result matches what you genuinely expect. Real testing frameworks like pytest build on this same basic idea, adding better reporting and organization, but the fundamental concept — call the function, assert the expected result — stays the same.

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

test_add()  # if any assertion fails, you immediately know exactly which one

Here’s the genuinely important practical benefit, beyond just “catching bugs”: tests let you change existing code with real confidence later. Without tests, modifying a function that other code depends on means hoping you didn’t break anything, checking manually, and possibly missing something. With a solid set of tests, you can make a change and immediately re-run the tests — if they all still pass, you have genuine, concrete evidence the change didn’t break existing expected behavior, not just an assumption.

Testing edge cases specifically — empty inputs, zero, negative numbers, unusually large values — is genuinely important, since these are exactly the cases most likely to reveal a bug that “normal” everyday inputs wouldn’t expose at all.

Decision Task

You’re about to refactor (rewrite the internals of) a function that many other parts of a real project depend on, with no existing tests for it. Before reading on: what real, concrete risk does this specific situation carry, beyond just “it might have a bug”?

Show Answer

Without existing tests, you have no fast, reliable way to confirm the refactored version still behaves identically to the original for every case that matters — you’d be relying purely on manual checking or hoping nothing breaks, rather than having concrete, immediate evidence. Writing tests BEFORE refactoring gives you a genuine safety net: run them after the change, and passing tests are real evidence the behavior is preserved, not just an assumption.

Common Mistake

Only testing “happy path” inputs — normal, expected values — while skipping edge cases like zero, negative numbers, empty inputs, or unusually large values. Bugs disproportionately hide in edge cases specifically, since normal inputs are exactly what a function was most carefully designed and manually checked against during initial development.

Practice Questions

1. Write an assert statement checking that a function called multiply(3, 4) returns 12.

Show Answer

assert multiply(3, 4) == 12

2. What happens if an assert statement’s condition is False?

Show Answer

It raises an AssertionError, immediately signaling that the expected behavior wasn’t met.

3. Why should tests specifically include edge cases like zero or negative numbers, not just “normal” values?

Show Answer

Bugs disproportionately hide in edge cases, since normal/expected inputs are usually what a function was most carefully designed and checked against already during development.

4. True or False: having a solid set of passing tests gives you concrete evidence a code change didn’t break existing behavior, not just an assumption.

Show Answer

True — this is the genuine, practical value of testing beyond just “catching bugs upfront,” specifically enabling confident future changes.

5. Write a small test function (using assert) verifying that a function called is_even correctly handles both an even and an odd number.

Show Answer

def test_is_even():\n assert is_even(4) == True\n assert is_even(7) == False

Try It Yourself

Without looking back, write a small set of assert-based tests for a function called divide(a, b) that returns a/b — include at least one edge case (like dividing by zero, expecting it to raise an error, or a normal case) reflecting this lesson’s emphasis on edge cases.

Show Answer

A reasonable answer includes normal cases like assert divide(10, 2) == 5, plus genuine edge-case thinking, such as considering what should happen with divide(5, 0) — testing that it correctly raises a ZeroDivisionError (from Part 3.4) rather than assuming it silently works, exactly the edge-case-first mindset this lesson emphasizes.

Quick Check

1. What does an assert statement do if its condition is True?

Show Answer

Nothing visible — it passes silently.

2. What does an assert statement do if its condition is False?

Show Answer

Raises an AssertionError.

3. What real practical benefit do tests provide beyond just catching bugs upfront?

Show Answer

They let you change existing code with genuine confidence later, since passing tests are concrete evidence a change didn’t break expected behavior.

4. Why are edge cases (zero, negative numbers, empty inputs) especially important to test?

Show Answer

Bugs disproportionately hide in edge cases, since normal inputs are usually already well-checked during initial development.

5. Name a real testing framework mentioned in this lesson that builds on the same basic assert concept.

Show Answer

pytest

تحميل هذا الباب / Download this Chapterنسخة كاملة للدراسة بدون إنترنت، مع الأسئلة والإجابات والصور المتاحة.