Typorithm · easy

Boundaries and test discovery

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

Step 1 of 4 · intro card

Boundaries and test discovery

0%

Concept and contract

Test both sides of a decision and make collection visible.

A threshold requirement creates partitions: values below the threshold and values at or above it. For an experience requirement of two years, one year is ineligible and two years is eligible. Testing twenty years alone cannot distinguish greater-than from greater-than-or-equal.

Use a named test module such as test_main.py and test functions beginning with test_. pytest collection is a separate stage that decides what will execute. A filename or function typo can leave a beautiful assertion completely unused. Check the number of collected items when introducing a suite.

Mechanism and worked example

The worked example checks one year and three years, making both branches visible before the boundary exercise.

The worked example checks one year and three years, making both branches visible before the boundary exercise. The implementation uses >= 2. In the playground, pytest --collect-only lists the named tests without running their assertions; pytest -q then performs the actual run.

For this first exercise the module is flat and imports no application package. In larger projects use an installed package or a deliberate import layout rather than modifying sys.path randomly. Discovery and import problems should be repaired explicitly, not hidden with broad exception handling.

Complete worked example

python

def eligible(years):
    return years >= 2


def test_below_threshold():
    assert eligible(1) is False


def test_above_threshold():
    assert eligible(3) is True
Reasoning
The worked example checks one year and three years, making both branches visible before the boundary exercise. The implementation uses >= 2. In the playground, pytest --collect-only lists the named tests without running their assertions; pytest -q then performs the actual run. For this first exercise the module is flat and imports no application package. In larger projects use an installed package or a deliberate import layout rather than modifying sys.path randomly. Discovery and import problems should be repaired explicitly, not hidden with broad exception handling.
Open in local Python playground ↗

Focused test decision: context is in the complete example

python

def test_exact_threshold():
    assert eligible(2) is True
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 bug accepts every positive value.

The controlled bug accepts every positive value. The below-threshold case catches it, even though the positive happy path still passes. Another subtler bug, > instead of >=, survives the two initial tests because neither tests the exact boundary.

This is why branch execution is weaker than decision coverage of meaningful boundaries. A test may execute a line and still fail to assert its critical policy. Naming a file helpers.py and assuming pytest will find its tests is another mistake: default discovery uses configured test patterns.

Controlled bug: the tests must report failure

python

def eligible(years):
    return years > 0


def test_below_threshold():
    assert eligible(1) is False


def test_above_threshold():
    assert eligible(3) is True
Reasoning
The controlled bug accepts every positive value. The below-threshold case catches it, even though the positive happy path still passes. Another subtler bug, > instead of >=, survives the two initial tests because neither tests the exact boundary. This is why branch execution is weaker than decision coverage of meaningful boundaries. A test may execute a line and still fail to assert its critical policy. Naming a file helpers.py and assuming pytest will find its tests is another mistake: default discovery uses configured test patterns.
Open in local Python playground ↗

Independent practice

Run collection and explain why there are initially two tests.

Run collection and explain why there are initially two tests. Then add the exact two-year case, with an expectation of True. Retain the one-year and three-year cases. Finally change >= to > temporarily and predict which test fails before running it.

Do not expand into arbitrary negative and float policies unless the requirement defines them. If non-negative whole years are the accepted domain, document that scope. Input validation can be a later independent behavior, with its own expected error and tests.

Step 1: Add the exact two-year eligibility boundary. 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

def eligible(years):
    return years >= 2


def test_below_threshold():
    assert eligible(1) is False


def test_above_threshold():
    assert eligible(3) is True

# Task: Add the exact two-year eligibility boundary.
Reasoning
Add the exact two-year eligibility boundary.
Open in local Python playground ↗
  • Hint 1: Compare the requirement’s “at least” with the operator.
  • Hint 2: Keep both original input partitions and add eligible(2).

Solution, tests and trade-offs

The solution contains three distinct examples for the decision boundary.

The solution contains three distinct examples for the decision boundary. Its acceptance probe changes >= to >. The original two tests remain green, while the new exact-boundary test fails. This checks the quality of the added regression test rather than only counting its lines.

pytest exit code five means no tests were collected, not that everything passed. Exit code two generally indicates an interrupted session, including a collection error. Read the error report rather than reducing all nonzero outcomes to a generic failed assertion.

Practice solution and acceptance checks

python

def eligible(years):
    return years >= 2


def test_below_threshold():
    assert eligible(1) is False


def test_above_threshold():
    assert eligible(3) is True

def test_exact_threshold():
    assert eligible(2) is True
Reasoning
The solution contains three distinct examples for the decision boundary. Its acceptance probe changes >= to >. The original two tests remain green, while the new exact-boundary test fails. This checks the quality of the added regression test rather than only counting its lines. pytest exit code five means no tests were collected, not that everything passed. Exit code two generally indicates an interrupted session, including a collection error. Read the error report rather than reducing all nonzero outcomes to a generic failed assertion.
Open in local Python playground ↗

Interview reasoning and next step

Explain the difference between a branch, a boundary and an input partition.

Explain the difference between a branch, a boundary and an input partition. Show the concrete mutant that the new test kills. If a CI job collects no tests, investigate discovery paths and names; do not accept that run as evidence that the product works.

The next step is parameterization: preserve distinct inputs and readable case IDs while avoiding repetitive test functions. Keep each expected answer grounded in the requirement rather than copied from the current implementation.

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-boundaries 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.

Test both sides of a decision and make collection visible.