Typorithm · medium

Fake the boundary where it is looked up

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

Step 1 of 4 · intro card

Fake the boundary where it is looked up

0%

Concept and contract

Inject a small callable instead of contacting a service.

A unit test should not call a real pricing, profile or job service to verify a formatting decision. Represent the external dependency by a small callable that returns a controlled response. This separates the consumer’s behavior from connectivity, credentials and third-party state.

Dependency injection is often simpler than patching: pass the callable explicitly. Where an existing module imports a name, patch the consumer’s lookup location, not merely the module where the original function was defined. Understand the reference that the code actually uses.

Mechanism and worked example

The complete report function accepts a fetch callable and extracts a count.

The complete report function accepts a fetch callable and extracts a count. The test supplies a fake that records its call argument and returns a known response. The assertions check the report text and that the intended company was requested once.

The fake has a deliberately small interface. It does not simulate an entire HTTP stack or make any network request. This keeps the test focused on the consumer contract. A separate integration or contract test is needed to show that the real client and upstream API still agree.

Complete worked example

python

def report(company, fetch):
    response = fetch(company)
    return f"{company}: {response['count']} roles"


def test_report_uses_requested_company():
    calls = []
    def fake(company):
        calls.append(company)
        return {"count": 3}
    assert report("Example", fake) == "Example: 3 roles"
    assert calls == ["Example"]
Reasoning
The complete report function accepts a fetch callable and extracts a count. The test supplies a fake that records its call argument and returns a known response. The assertions check the report text and that the intended company was requested once. The fake has a deliberately small interface. It does not simulate an entire HTTP stack or make any network request. This keeps the test focused on the consumer contract. A separate integration or contract test is needed to show that the real client and upstream API still agree.
Open in local Python playground ↗

Focused test decision: context is in the complete example

python

calls = []
def fake(company):
    calls.append(company)
    return {"count": 3}
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 asks for the wrong company.

The controlled implementation asks for the wrong company. The call-record assertion fails even if the returned count still makes the visible report look correct. Checking only that fetch was called would miss this argument error.

Over-mocking every internal helper locks tests to implementation details. Record the important boundary arguments, but avoid asserting an exact sequence of private calls unless that sequence is itself a required protocol. A refactor should not break a test solely because an unobservable helper was extracted.

Controlled bug: the tests must report failure

python

def report(company, fetch):
    response = fetch("Wrong")
    return f"{company}: {response['count']} roles"


def test_report_uses_requested_company():
    calls = []
    def fake(company):
        calls.append(company)
        return {"count": 3}
    assert report("Example", fake) == "Example: 3 roles"
    assert calls == ["Example"]
Reasoning
The controlled implementation asks for the wrong company. The call-record assertion fails even if the returned count still makes the visible report look correct. Checking only that fetch was called would miss this argument error. Over-mocking every internal helper locks tests to implementation details. Record the important boundary arguments, but avoid asserting an exact sequence of private calls unless that sequence is itself a required protocol. A refactor should not break a test solely because an unobservable helper was extracted.
Open in local Python playground ↗

Independent practice

Add a zero-count response case with a fresh fake and assert the exact report.

Add a zero-count response case with a fresh fake and assert the exact report. Keep the existing call-argument assertion. Zero vacancies is a real value, distinct from a missing field or an upstream failure.

Type the callable and the final assertion before considering a mock library. Then deliberately introduce a fallback of one when count is zero. Predict that the old positive example remains green while the new test detects the invented vacancy.

Step 1: Add a zero-count report test with an injected fake and no network calls. 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 report(company, fetch):
    response = fetch(company)
    return f"{company}: {response['count']} roles"


def test_report_uses_requested_company():
    calls = []
    def fake(company):
        calls.append(company)
        return {"count": 3}
    assert report("Example", fake) == "Example: 3 roles"
    assert calls == ["Example"]

# Task: Add a zero-count report test with an injected fake and no network calls.
Reasoning
Add a zero-count report test with an injected fake and no network calls.
Open in local Python playground ↗
  • Hint 1: Return {"count": 0} from the fake.
  • Hint 2: Assert both the requested company and the exact zero report.

Solution, tests and trade-offs

The solution adds the zero-count test.

The solution adds the zero-count test. The practice probe replaces a zero result with one by using truthiness. The new assertion rejects it. The fake returns a controlled response without reaching a service, so the result is deterministic and costs nothing to run.

This does not test retries, timeout behavior, HTTP parsing or upstream authentication. Keep those as separate boundaries with appropriate owned fixtures. Do not interpret a passing unit fake as permission to skip every real-client compatibility check.

Practice solution and acceptance checks

python

def report(company, fetch):
    response = fetch(company)
    return f"{company}: {response['count']} roles"


def test_report_uses_requested_company():
    calls = []
    def fake(company):
        calls.append(company)
        return {"count": 3}
    assert report("Example", fake) == "Example: 3 roles"
    assert calls == ["Example"]

def test_zero_count_is_not_replaced():
    def fake(company):
        assert company == "Empty"
        return {"count": 0}
    assert report("Empty", fake) == "Empty: 0 roles"
Reasoning
The solution adds the zero-count test. The practice probe replaces a zero result with one by using truthiness. The new assertion rejects it. The fake returns a controlled response without reaching a service, so the result is deterministic and costs nothing to run. This does not test retries, timeout behavior, HTTP parsing or upstream authentication. Keep those as separate boundaries with appropriate owned fixtures. Do not interpret a passing unit fake as permission to skip every real-client compatibility check.
Open in local Python playground ↗

Interview reasoning and next step

Explain what this test proves about the consumer and what it does not prove about the upstream service.

Explain what this test proves about the consumer and what it does not prove about the upstream service. Show where to patch an imported symbol in a two-module application. Distinguish an expectation about a public boundary argument from an incidental assertion about private implementation calls.

Next inspect logs as structured records. Diagnostics should retain useful evidence without becoming a hidden dependency on exact formatting or sensitive payload text.

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

Inject a small callable instead of contacting a service.