Testing with pytest

code-quality
Use assertions, pytest, and edge cases to check behavior.
  • Level: Beginner to intermediate
  • Estimated time: 35–50 minutes
  • You will learn: Use assertions, pytest, and edge cases to check behavior.
  • Practice in: VS Code, terminal, pytest, and ruff

Questions

  • What problem does Testing with pytest help us solve in a small Python program?
  • What should we predict before running the example?
  • What value, output, or error should we inspect after changing one line?

Objectives

  • Run a complete example for testing with pytest in Colab.
  • Explain the example line by line using plain language.
  • Change one part of the code and predict the result before running it.
  • Recognize one common mistake and use the error message as evidence.

Hands-on episode: Testing with pytest

A pytest test is a small function whose assertions describe expected behavior. Good tests cover ordinary cases, edge cases, and mistakes you want to prevent from returning.

We will learn this by running code, not by memorizing a definition first. Open the Colab notebook from the button above, find this section, and run each cell in order. Keep a small note beside the notebook with three columns: prediction, actual result, and what changed.

Example 1.1

Read the assertion as a sentence: normalizing this name should produce that result.

def normalize_name(name):
return name.strip().title()

def test_normalize_name():
assert normalize_name(" ada ") == "Ada"

Run the cell once without editing it. If the result is different from your prediction, leave the prediction visible and write one sentence about the difference. That sentence is more useful than a perfect first guess.

Explain Example 1.1

  • The production function has one responsibility: clean and format a name.
  • The test function starts with test_, so pytest knows to run it.
  • The assertion compares actual output with expected output.
  • If someone removes .strip(), this test fails and explains what behavior changed.

Now explain the example out loud or in a Markdown cell. Use short sentences: “this line creates…”, “this name stores…”, “this output appears because…”. If you cannot explain a line yet, run only the lines above it and inspect the values that exist at that moment.

Challenge 1.1

NoteChallenge

Add a test for "GRACE" and another for an empty string. The goal is not many tests for their own sake; it is examples that protect important promises.

Show a safe way to approach the challenge
  1. Copy Example 1.1 into a new Colab cell.
  2. Change exactly one value, name, condition, or line.
  3. Write the expected output before running the cell.
  4. Run the cell and compare the actual result with your prediction.
  5. If the result surprises you, undo the change and try a smaller one.

Suggested first move: Add a test for "GRACE" and another for an empty string.

Debugging checkpoint 1.1

WarningDebugging checkpoint

Avoid testing only the example you happened to try manually. Also avoid testing implementation details that could change while behavior stays correct. Start with inputs and outputs a user would care about.

Do not debug by rewriting the whole example. Read the error type or surprising output, inspect the closest value with print(...) or type(...), then change one thing. This is the same routine you will use in larger projects.

Apply it

Write tests for first(items), average(numbers), or normalize_name(name). Include one normal case and one edge case before editing the implementation.

Finish by adding a Markdown cell that answers: What did this example teach me that I can reuse in a project?

Key points

  • Learn the concept by running a complete, small example first.
  • Predict before execution so your thinking becomes visible.
  • Change one thing at a time so cause and effect stay clear.
  • Treat errors as clues about the exact line or value Python could not handle.

Why this matters

Use assertions, pytest, and edge cases to check behavior.

This lesson combines related subtopics that belong together in one learning conversation. You will still pause for a quiz after each section, but you do not need to jump between separate pages while building one clear explanation.

NoteGuiding questions

By the end of this lesson, you should be able to answer:

  • How do the sections in Testing with pytest fit together?
  • Which small example demonstrates each section?
  • Which debugging clue should I check first for each section?
NoteLearning objectives

You will practice how to:

  • explain the shared concept for this lesson;
  • use each section as one step in a larger workflow;
  • complete 3 short section quizzes before moving on;
  • connect examples, mistakes, and debugging routines.

Lesson map

  • 1. Assertions — Use assert statements to document and check assumptions while learning and testing.
  • 2. pytest — Write and run automated tests with pytest.
  • 3. Edge Cases — Choose tests for boundaries, empty inputs, and unusual but valid situations.

1. Assertions

Use assert statements to document and check assumptions while learning and testing.

TipAnalogy

An assertion is a checkpoint gate: if the rule is false, the gate stops you immediately.

What this means

An assertion says a condition should be true at this point in the program.

Example 1

Predict what will happen before you run the code.

total = 2 + 3
assert total == 5

Step-by-step explanation

  1. total = 2 + 3 — pause here and say what this line reads, creates, changes, or displays.
  2. assert total == 5 — pause here and say what this line reads, creates, changes, or displays.

After running the example, compare the actual output with your prediction. If they differ, do not erase your prediction. The difference is the part that can teach you the most.

Challenge

NotePractice

Change one input value, predict the new output, run the code, and explain the difference in one sentence.

Show one possible solution path
  1. Copy Example 1 into Colab, Jupyter, or a .py file.
  2. Mark the line you plan to change.
  3. Write a one-sentence prediction.
  4. Run the changed code.
  5. If the result surprises you, restore the original and change a smaller part.

The goal is not to find the only correct answer. The goal is to create a small experiment where you can explain cause and effect.

Common mistakes

WarningCommon mistake

Do not use assert for user-facing validation in production programs; assertions can be disabled.

When you get stuck, use this debugging routine:

  1. Read the last line of the error message or inspect the unexpected output.
  2. Find the smallest line of code that could be responsible.
  3. Print or inspect the value and type at that point.
  4. Change one thing.
  5. Run again and record what changed.

Check your understanding

This quiz checks the ideas in this section before you move on.

2. pytest

Write and run automated tests with pytest.

TipAnalogy

pytest is a tireless practice partner: it repeats your checks every time you change the code.

What this means

pytest discovers test functions and reports which expectations pass or fail.

Example 2

Predict what will happen before you run the code.

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

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

Step-by-step explanation

  1. def add(a, b): — pause here and say what this line reads, creates, changes, or displays.
  2. return a + b — pause here and say what this line reads, creates, changes, or displays.
  3. def test_add(): — pause here and say what this line reads, creates, changes, or displays.
  4. assert add(2, 3) == 5 — pause here and say what this line reads, creates, changes, or displays.

After running the example, compare the actual output with your prediction. If they differ, do not erase your prediction. The difference is the part that can teach you the most.

Challenge

NotePractice

Change one input value, predict the new output, run the code, and explain the difference in one sentence.

Show one possible solution path
  1. Copy Example 1 into Colab, Jupyter, or a .py file.
  2. Mark the line you plan to change.
  3. Write a one-sentence prediction.
  4. Run the changed code.
  5. If the result surprises you, restore the original and change a smaller part.

The goal is not to find the only correct answer. The goal is to create a small experiment where you can explain cause and effect.

Common mistakes

WarningCommon mistake

A test file or test function with the wrong name may not be discovered. Use test_ prefixes.

When you get stuck, use this debugging routine:

  1. Read the last line of the error message or inspect the unexpected output.
  2. Find the smallest line of code that could be responsible.
  3. Print or inspect the value and type at that point.
  4. Change one thing.
  5. Run again and record what changed.

Check your understanding

This quiz checks the ideas in this section before you move on.

3. Edge Cases

Choose tests for boundaries, empty inputs, and unusual but valid situations.

TipAnalogy

Edge cases are the corners of a tablecloth: if the corners are secure, the middle is usually easier.

What this means

An edge case is an input near a boundary where assumptions often break.

Example 3

Predict what will happen before you run the code.

def first(items):
    if not items:
        return None
    return items[0]

assert first([]) is None

Step-by-step explanation

  1. def first(items): — pause here and say what this line reads, creates, changes, or displays.
  2. if not items: — pause here and say what this line reads, creates, changes, or displays.
  3. return None — pause here and say what this line reads, creates, changes, or displays.
  4. return items[0] — pause here and say what this line reads, creates, changes, or displays.
  5. assert first([]) is None — pause here and say what this line reads, creates, changes, or displays.

After running the example, compare the actual output with your prediction. If they differ, do not erase your prediction. The difference is the part that can teach you the most.

Challenge

NotePractice

Change one input value, predict the new output, run the code, and explain the difference in one sentence.

Show one possible solution path
  1. Copy Example 1 into Colab, Jupyter, or a .py file.
  2. Mark the line you plan to change.
  3. Write a one-sentence prediction.
  4. Run the changed code.
  5. If the result surprises you, restore the original and change a smaller part.

The goal is not to find the only correct answer. The goal is to create a small experiment where you can explain cause and effect.

Common mistakes

WarningCommon mistake

Only testing typical examples can leave boundary bugs hidden until users find them.

When you get stuck, use this debugging routine:

  1. Read the last line of the error message or inspect the unexpected output.
  2. Find the smallest line of code that could be responsible.
  3. Print or inspect the value and type at that point.
  4. Change one thing.
  5. Run again and record what changed.

Check your understanding

This quiz checks the ideas in this section before you move on.

Notebook and Colab practice

Open a blank notebook at https://colab.new. Use one section at a time: copy the Example 1, predict the result, run it, answer the section quiz, and then move to the next section. This is better than copying the entire page at once.

Instructor note

Teaching notes
  • Treat each section as a short teaching episode.
  • Pause for the section quiz before introducing the next section.
  • Ask learners to compare sections: what stayed the same, and what changed?
  • If time is short, teach the first two sections live and assign the rest as practice.

Key points

TipKey points
  • Assertions: An assertion says a condition should be true at this point in the program.
  • pytest: pytest discovers test functions and reports which expectations pass or fail.
  • Edge Cases: An edge case is an input near a boundary where assumptions often break.
  • Use the section quizzes as gates: review before moving on if a quiz feels uncertain.

References

  • pytest documentation: https://docs.pytest.org/
  • PEP 8 style guide: https://peps.python.org/pep-0008/
  • Ruff documentation: https://docs.astral.sh/ruff/
  • mypy documentation: https://mypy.readthedocs.io/
  • Python Tutorial: https://docs.python.org/3/tutorial/
  • Quarto OJS documentation: https://quarto.org/docs/interactive/ojs/
  • ipywidgets documentation: https://ipywidgets.readthedocs.io/en/stable/
Back to top