Fakes versus mocks
A mock records how your code talked to a collaborator. A fake is a small working version of the collaborator: a dict standing in for a database, or a clock you move by hand. A test with a fake checks results, not calls, so it keeps passing when the code is refactored to make different calls that do the same job.
class FakeClock:
def __init__(self, start=0.0):
self.now = start
def __call__(self):
return self.now
def advance(self, seconds):
self.now += seconds
clock = FakeClock()
clock.advance(90)
print(clock())Code that takes its clock as a parameter, with time.monotonic as the real one, can be tested this way: no waiting, and time moves exactly as far as the test says.
Why not a mock
from unittest.mock import Mock
clock = Mock(side_effect=[0.0, 30.0])
print(clock(), clock())
try:
clock()
except StopIteration:
print("a third read breaks the test")A mock scripted with readings breaks as soon as the code reads the clock once more, though nothing a user sees has changed.
Your task
Sessions is written for you. Write FakeClock(start=0.0), whose instances return the current time when called and move it forward with advance(seconds). Then write test_active_before_ttl and test_expired_at_ttl for Sessions(60, clock) with a FakeClock, advancing it by 59 and by exactly 60 seconds.
The explanation opens here once every check passes.