Mocks and test doubles — DedSec Project
Test doubles — injected clocks and transports with observable calls Scenario and independent contract A local client fetches a document by slug but must not contact its transport after the…
Test doubles — injected clocks and transports with observable calls
Scenario and independent contract
A local client fetches a document by slug but must not contact its transport after the deadline. Invalid slugs are rejected, responses must contain a title, and a timeout leaves the transport untouched. Unit tests should not make real network requests or depend on wall-clock sleeps. Inject a clock and transport interface so fixtures can advance time deterministically and inspect the exact requested slug.
Executable Python 3 lab
Save as mock_lab.py and run python3 mock_lab.py. The fake transport records calls instead of sending packets. The fake clock starts at zero and can be moved without waiting.
class FakeClock:
def __init__(self):
self.tick = 0
def now(self):
return self.tick
class FakeTransport:
def __init__(self, answer):
self.answer = answer
self.calls = []
def request(self, slug):
self.calls.append(slug)
return self.answer
def fetch(slug, transport, clock, deadline):
if clock.now() > deadline:
raise TimeoutError("deadline exceeded")
if not isinstance(slug,str) or not slug.isascii() or not slug.isalnum():
raise ValueError("invalid slug")
value = transport.request(slug)
if not isinstance(value,dict) or "title" not in value:
raise ValueError("invalid response")
return value["title"]
clock = FakeClock()
stub = FakeTransport({"title":"local"})
assert fetch("abc2",stub,clock,10) == "local"
assert stub.calls == ["abc2"]
clock.tick = 11
try:
fetch("abc2",stub,clock,10)
except TimeoutError:
pass
else:
raise AssertionError("deadline bypassed")
assert stub.calls == ["abc2"]
print("PASS: injected clock, transport and call history")
What is a mock, stub or fake?
A stub supplies canned responses. A fake provides a small working implementation of a collaborator. A mock primarily verifies interactions, such as whether request was called exactly once. Our FakeTransport combines canned responses with a call history; labels are less important than the test's explicit expectations. Tests that merely duplicate the implementation's own branching logic are weak: write the deadline and input contracts separately before coding. Contract tests against a real transport implementation are still needed because injected fakes cannot discover protocol incompatibilities.
Failure-oriented exercises
- Change the fake response to an empty dictionary and ensure validation raises rather than returning success.
- Try a slug containing a slash, space or Unicode character. Decide whether your application wants a different explicit slug policy.
- Move the clock to exactly the deadline, then one tick later. Document whether the rule uses greater-than or greater-than-or-equal.
- Create a faulty implementation that sends before checking the clock. The existing call-history assertion should fail; restore correct ordering afterwards.
Evidence: PASS, an unchanged call count on timeout, invalid-response test, and one boundary convention for the deadline.