Test generator failures at iteration time instead of construction
Test generator failures at iteration time instead of construction
Test a generator failure by advancing the generator inside assertRaises, not by calling the generator function. Calling fails_before_yield() below creates a generator object successfully; its ValueError occurs only on the first next(). The contrasting generator yields "ready" once, then raises on its second advancement. The printed boundaries make both timings explicit.
A generator function does not execute its body merely because it is called. Its generator iterator resumes when an iteration method advances it, and the Python generator-iterator reference explains that next() resumes execution until the next yield or termination. Therefore, an assertion around construction would miss a body exception in this situation. Create the iterator first, then put the precise advance expected to fail inside the assertion context.
This distinction helps make test intent readable. A first-advance failure can represent validation deferred by a lazy API; a later failure can represent a bad item encountered after usable output. The example checks exception messages as well as yielded values, so a change in either boundary fails deterministically. It does not assert anything about generator cleanup, send(), throw(), or resource release; those require separate cases if the API uses them. Basic generator behavior used here has no newer-version requirement.
AI assistance disclosure: this article was drafted with AI assistance and runs only synthetic generator code.
import unittest
def fails_before_yield():
raise ValueError("missing header")
yield "unreachable"
def yields_then_fails():
yield "ready"
raise ValueError("bad trailer")
case = unittest.TestCase()
first_boundary = fails_before_yield()
assert iter(first_boundary) is first_boundary
with case.assertRaises(ValueError) as first_error:
next(first_boundary)
assert str(first_error.exception) == "missing header"
later_boundary = yields_then_fails()
assert next(later_boundary) == "ready"
with case.assertRaises(ValueError) as later_error:
next(later_boundary)
assert str(later_error.exception) == "bad trailer"
print("construction: no exception")
print(f"first next: {first_error.exception}")
print(f"second next after ready: {later_error.exception}")
construction: no exception
first next: missing header
second next after ready: bad trailer