A useful test distinguishes correct behavior from a plausible failure. Test boundaries and invariants, then deliberately introduce a defect and ensure the test fails. Avoid checking only that a function returned something.
Inspect the fake sleeper rather than waiting for real time to pass.
import unittest
class Contract(unittest.TestCase):
def runTest(self):
self.assertEqual(sum([1, 2, 3]), 6)
with self.assertRaises(ValueError):
int("bad")
result = unittest.TestResult()
Contract().run(result)
print(result.wasSuccessful())Inject effects as arguments: a clock, sleeper, random generator or request adapter. A fake can record calls and deterministically return or raise. This makes failure paths testable without a server, network, API key or actual wait.
waits = []
def backoff(attempt, sleep):
sleep(0.1 * 2**attempt)
backoff(2, waits.append)
print(waits) # [0.4]The build retries only TimeoutError. attempts counts total calls, including
the initial call. After the last failure, raise that error and do not sleep.
Nonretryable exceptions propagate immediately. Retrying every exception can
conceal a programming error; sleeping after a terminal failure wastes time.
Use separate failure cases to test retry exhaustion and nonretryable exceptions.
Assert the returned value, adapter call count, sleep sequence and final error. A successful None is still a successful result. The sleeper is synchronous here; a later skill introduces async concurrency and cancellation. These are local policy exercises, not production API availability guarantees.