Typorithm · easy

A test is an observable contract

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

Step 1 of 4 · intro card

A test is an observable contract

0%

Concept and contract

Turn a requirement into a named example with an independent expected value.

A useful test distinguishes correct behavior from a plausible mistake. Start with a tiny requirement: delivery costs three credits per item. The input is two items and the independently calculated expected cost is six. This test is a statement about the public result, not about how many lines the implementation has.

Write the example on paper before calling the function. If the expected expression repeats the same implementation, both sides can share the same bug. A test is executable evidence for a particular contract, not a proof of every input.

Mechanism and worked example

The complete module places the small function beside its first test so the whole example can run unchanged.

The complete module places the small function beside its first test so the whole example can run unchanged. In a project, move the function into its own module and import it. Arrange a meaningful input, perform the action once, then assert its observable result. Naming the test after the behavior makes the failure useful to someone who did not write it.

pytest rewrites assertions in collected test modules to explain differences. Run tests rather than merely running this file with Python: defining a test function does not call it. The playground shows actual collection and test-phase reports.

Complete worked example

python

def delivery_cost(items):
    return items * 3


def test_two_items_cost_six():
    assert delivery_cost(2) == 6
Reasoning
The complete module places the small function beside its first test so the whole example can run unchanged. In a project, move the function into its own module and import it. Arrange a meaningful input, perform the action once, then assert its observable result. Naming the test after the behavior makes the failure useful to someone who did not write it. pytest rewrites assertions in collected test modules to explain differences. Run tests rather than merely running this file with Python: defining a test function does not call it. The playground shows actual collection and test-phase reports.
Open in local Python playground ↗

Focused test decision: context is in the complete example

python

def test_two_items_cost_six():
    assert delivery_cost(2) == 6
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 mistake changes multiplication into addition.

The controlled mistake changes multiplication into addition. Both implementations return numbers, and both accept the happy-path input, but only one meets the requirement. The assertion compares against six and reports the incorrect five. An assertion such as assert delivery_cost(2) would accept either nonzero result.

Avoid catching AssertionError around a test simply to print an explanation. That turns a failed condition into a successful test. Also avoid testing a private temporary variable when the external result is the actual contract. Refactoring should not force irrelevant test changes.

Controlled bug: the tests must report failure

python

def delivery_cost(items):
    return items + 3


def test_two_items_cost_six():
    assert delivery_cost(2) == 6
Reasoning
The controlled mistake changes multiplication into addition. Both implementations return numbers, and both accept the happy-path input, but only one meets the requirement. The assertion compares against six and reports the incorrect five. An assertion such as assert delivery_cost(2) would accept either nonzero result. Avoid catching AssertionError around a test simply to print an explanation. That turns a failed condition into a successful test. Also avoid testing a private temporary variable when the external result is the actual contract. Refactoring should not force irrelevant test changes.
Open in local Python playground ↗

Independent practice

First type the three-line test, including its name and literal expectation.

First type the three-line test, including its name and literal expectation. Run it, then inspect the failed counterexample. For the independent task, add the zero-items case without removing the existing two-items case. Zero is a boundary with a precise business meaning: no delivered items incur no per-item charge.

Make only this small addition before considering negative quantities or non-integer validation. Those are separate requirements; inventing them inside a test silently changes the API. Explain which input domain the current function supports and what is still unspecified.

Step 1: Add a separate test that zero items cost zero. 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 delivery_cost(items):
    return items * 3


def test_two_items_cost_six():
    assert delivery_cost(2) == 6

# Task: Add a separate test that zero items cost zero.
Reasoning
Add a separate test that zero items cost zero.
Open in local Python playground ↗
  • Hint 1: Write the expected value before running the function.
  • Hint 2: Keep the two-items test; add test_no_items_cost_nothing.

Solution, tests and trade-offs

The solution adds a separate zero test.

The solution adds a separate zero test. The acceptance check also substitutes a function that returns a minimum charge only for zero: the original happy-path test stays green, while the new boundary test fails. This verifies that the added test can observe its intended regression.

One killed mutation does not imply mutation completeness. Keep the example small enough to reason about and add further cases only for distinct decisions or documented edge conditions. Prefer clear failing output over a loop that hides which case caused the failure.

Practice solution and acceptance checks

python

def delivery_cost(items):
    return items * 3


def test_two_items_cost_six():
    assert delivery_cost(2) == 6

def test_no_items_cost_nothing():
    assert delivery_cost(0) == 0
Reasoning
The solution adds a separate zero test. The acceptance check also substitutes a function that returns a minimum charge only for zero: the original happy-path test stays green, while the new boundary test fails. This verifies that the added test can observe its intended regression. One killed mutation does not imply mutation completeness. Keep the example small enough to reason about and add further cases only for distinct decisions or documented edge conditions. Prefer clear failing output over a loop that hides which case caused the failure.
Open in local Python playground ↗

Interview reasoning and next step

In an interview, explain the contract, why the expected value is independent, and a concrete wrong implementation that the test rejects.

In an interview, explain the contract, why the expected value is independent, and a concrete wrong implementation that the test rejects. Distinguish discovering a test from executing its assertion. Mention that a green run with zero collected tests is not success for a required suite.

Next add boundaries and named parameter cases. The desktop trainer builds typing and recall; the browser playground performs execution. Neither a typing score nor a single passing test certifies that an application is production-ready.

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

Turn a requirement into a named example with an independent expected value.