Typorithm · medium

Capstone: a regression suite that earns its green run

pythonpytesttestingindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

Capstone: a regression suite that earns its green run

0%

Concept and contract

Combine validation, boundaries, side-effect checks and a controlled mutation.

A useful green run means that the intended tests were collected and their stated contracts held. It does not mean the program has no bugs. This capstone filters owned job records by minimum experience and retains the input without modification. The scope is a small pure selection function, not a live feed parser.

State the contracts before writing the suite: invalid negative thresholds raise ValueError; qualifying records preserve their input order; the original list is not changed; and zero has its documented inclusive meaning. Each responsibility deserves an independently understandable assertion.

Mechanism and worked example

The worked module selects records into a new list.

The worked module selects records into a new list. One test checks the selected ID and unchanged input; another checks the invalid threshold. Literal expected IDs avoid repeating the comprehension as the oracle. The tests use only synthetic records with an explicit experience field.

Run collection, then the suite, and read the phase outcomes. The playground’s syntax check only compiles source; it cannot replace these assertions. Likewise a typing completion record is not the same thing as having executed the regression suite.

Complete worked example

python

import copy
import pytest


def select_roles(records, minimum):
    if minimum < 0:
        raise ValueError("minimum must be nonnegative")
    return [row for row in records if row["years"] >= minimum]


def test_selection_preserves_input():
    records = [{"id": "junior", "years": 1}, {"id": "senior", "years": 3}]
    before = copy.deepcopy(records)
    assert [r["id"] for r in select_roles(records, 2)] == ["senior"]
    assert records == before


def test_negative_minimum_rejected():
    with pytest.raises(ValueError, match="nonnegative"):
        select_roles([], -1)
Reasoning
The worked module selects records into a new list. One test checks the selected ID and unchanged input; another checks the invalid threshold. Literal expected IDs avoid repeating the comprehension as the oracle. The tests use only synthetic records with an explicit experience field. Run collection, then the suite, and read the phase outcomes. The playground’s syntax check only compiles source; it cannot replace these assertions. Likewise a typing completion record is not the same thing as having executed the regression suite.
Open in local Python playground ↗

Focused test decision: context is in the complete example

python

assert [r["id"] for r in select_roles(records, 0)] == ["starter", "next"]
Focused test decision
Type this short test decision after understanding the complete runnable module. Desktop typing records practice; Run tests in the local playground executes real pytest.

Counterexamples and common mistakes

The controlled implementation changes >= to < and the selection assertion fails.

The controlled implementation changes >= to < and the selection assertion fails. A high line-coverage number could still occur if the test called the function but never checked the returned IDs. Coverage reports executed code; they do not measure the quality of every expectation.

A single mutation result has limits too. Some mutations are equivalent under the documented domain, while others expose missing behavior. Use a controlled plausible defect to question a test, not a target percentage that encourages meaningless assertions.

Controlled bug: the tests must report failure

python

import copy
import pytest


def select_roles(records, minimum):
    if minimum < 0:
        raise ValueError("minimum must be nonnegative")
    return [row for row in records if row["years"] < minimum]


def test_selection_preserves_input():
    records = [{"id": "junior", "years": 1}, {"id": "senior", "years": 3}]
    before = copy.deepcopy(records)
    assert [r["id"] for r in select_roles(records, 2)] == ["senior"]
    assert records == before


def test_negative_minimum_rejected():
    with pytest.raises(ValueError, match="nonnegative"):
        select_roles([], -1)
Reasoning
The controlled implementation changes >= to < and the selection assertion fails. A high line-coverage number could still occur if the test called the function but never checked the returned IDs. Coverage reports executed code; they do not measure the quality of every expectation. A single mutation result has limits too. Some mutations are equivalent under the documented domain, while others expose missing behavior. Use a controlled plausible defect to question a test, not a target percentage that encourages meaningless assertions.
Open in local Python playground ↗

Independent practice

Add a zero-threshold case with zero- and one-year records.

Add a zero-threshold case with zero- and one-year records. Expect both IDs in input order and confirm the input remains unchanged. This extends the existing suite with one meaningful boundary rather than adding a random pile of cases.

First write the expected IDs, then the call and assertions. Run the original implementation, then the provided mutant that secretly enforces at least one year. Explain why the earlier two-year case and negative validation can remain green.

Step 1: Add the inclusive zero-threshold case and verify input ownership. Step 2: Keep the existing passing cases. Add the smallest new assertion first; predict the failure before running pytest.

Practice starter: solve before reading the solution

python

import copy
import pytest


def select_roles(records, minimum):
    if minimum < 0:
        raise ValueError("minimum must be nonnegative")
    return [row for row in records if row["years"] >= minimum]


def test_selection_preserves_input():
    records = [{"id": "junior", "years": 1}, {"id": "senior", "years": 3}]
    before = copy.deepcopy(records)
    assert [r["id"] for r in select_roles(records, 2)] == ["senior"]
    assert records == before


def test_negative_minimum_rejected():
    with pytest.raises(ValueError, match="nonnegative"):
        select_roles([], -1)

# Task: Add the inclusive zero-threshold case and verify input ownership.
Reasoning
Add the inclusive zero-threshold case and verify input ownership.
Open in local Python playground ↗
  • Hint 1: Use records with zero and one years so the boundary is visible.
  • Hint 2: Check ordered IDs and compare records with the independent deep copy.

Solution, tests and trade-offs

The solution adds the inclusive zero-boundary test.

The solution adds the inclusive zero-boundary test. The practice probe replaces the threshold with max(minimum, 1); only the new boundary exposes it. The worked module, solution and controlled counterexamples are run with real pytest in the downloadable lab and browser runtime.

The sample does not claim support for missing fields, malformed records, multi-country normalization or duplicate matching. Add those only after defining their contracts. A testing course should teach precise evidence rather than make an unrelated production parser appear verified.

Practice solution and acceptance checks

python

import copy
import pytest


def select_roles(records, minimum):
    if minimum < 0:
        raise ValueError("minimum must be nonnegative")
    return [row for row in records if row["years"] >= minimum]


def test_selection_preserves_input():
    records = [{"id": "junior", "years": 1}, {"id": "senior", "years": 3}]
    before = copy.deepcopy(records)
    assert [r["id"] for r in select_roles(records, 2)] == ["senior"]
    assert records == before


def test_negative_minimum_rejected():
    with pytest.raises(ValueError, match="nonnegative"):
        select_roles([], -1)

def test_zero_minimum_includes_zero_experience():
    records = [{"id": "starter", "years": 0}, {"id": "next", "years": 1}]
    before = copy.deepcopy(records)
    assert [r["id"] for r in select_roles(records, 0)] == ["starter", "next"]
    assert records == before
Reasoning
The solution adds the inclusive zero-boundary test. The practice probe replaces the threshold with max(minimum, 1); only the new boundary exposes it. The worked module, solution and controlled counterexamples are run with real pytest in the downloadable lab and browser runtime. The sample does not claim support for missing fields, malformed records, multi-country normalization or duplicate matching. Add those only after defining their contracts. A testing course should teach precise evidence rather than make an unrelated production parser appear verified.
Open in local Python playground ↗

Interview reasoning and next step

In an interview, walk through the requirement, a boundary, an independent oracle and a concrete rejected mutation.

In an interview, walk through the requirement, a boundary, an independent oracle and a concrete rejected mutation. Explain the difference between collection error, assertion failure, setup error and teardown error. Discuss when a fixture, a fake or an integration test owns the relevant dependency.

Next build a small real package and run the same suite in CI with pinned dependencies. Use the course’s local lab for that workflow; the browser intentionally provides Python and tests without an operating-system shell, remote execution or sharing.

Optional stretch — after the core task works: Introduce one deliberate implementation mistake and explain which assertion catches it. A green run alone is not evidence that every possible bug is covered.

Computer lab: https://applydjinn.com/typorithm/pytest-lab-20261106.zip. Run verify.py in an isolated environment. Worked examples and solutions pass, controlled counterexamples fail, and the new practice assertions reject a mutation missed by the baseline. Run --practice pyq-capstone after changing the supplied exercise. Python executes on your computer or in the browser, without submitting code to a server. Native question progress is a separate learning record.

Combine validation, boundaries, side-effect checks and a controlled mutation.