Testing FastAPI Lifespan Events
FastAPI lifespan events only run when TestClient is used as a context manager. Use this to test startup logic like DB pools before endpoints. Using TestClient(app) without with skips lifespan, leaving your app uninitialized and tests silently wrong.
Why it exists
FastAPI applications often need to perform work before the first request arrives and cleanup after the last one leaves. Database engines must be created, connection pools opened, caches populated, and message queue consumers started. Without a reliable way to test this lifecycle, integration tests would exercise endpoints against an uninitialized application that does not match production behavior. The lifespan protocol exists to wrap the entire application lifetime in a single async context manager, and testing it requires the test harness to respect that same boundary.
The mental model
Think of lifespan as the mains power strip for your application. When you flip the switch, everything boots in order; when you flip it off, teardown runs in reverse. TestClient without a with statement is like probing a circuit with the power off: the wires are there, but nothing is running. Using with TestClient(app) as client is flipping the switch, giving you a live system where startup has finished and shutdown is guaranteed to run when the block ends.
How it works
You define an async context manager decorated with asynccontextmanager. The code before yield is startup, and the code after yield is shutdown. You pass this lifespan function to your FastAPI app. In tests, you instantiate TestClient inside a with statement. Entering the block triggers the startup phase, so any state populated there is visible to your test. Exiting the block triggers the shutdown phase, so resources are released and global state is reset. If you call client.get outside the with block after the client has closed, the lifespan has already torn down.
When to use it
Use this pattern whenever your application relies on state or resources created during startup. Examples include SQLAlchemy engine initialization, Redis connection pool warming, loading machine learning models into memory, starting background task processors, or populating in-memory lookup tables. It is also essential when testing endpoints that read from state set by lifespan, because those endpoints will fail or return wrong data if the state is missing.
When not to use it
Do not use the context manager form when you are testing code that deliberately avoids lifespan, such as utility functions or standalone Pydantic models. If your test only checks path operation logic and never touches startup-dependent state, the overhead is unnecessary. Also avoid nesting multiple TestClient context managers in the same test without careful state management, because overlapping lifespans can create conflicting global state or connection pool exhaustion.
One canonical example
You have a lifespan that inserts two items into a shared dictionary before yield and clears the dictionary after yield. Outside the with TestClient block, the dictionary is empty. Inside the block, you assert the dictionary contains the seeded items, then call client.get to read them through the endpoint. When the block ends, the dictionary is empty again. This proves startup ran, the endpoint saw the data, and shutdown cleaned up.
Interview question
Your endpoint reads state populated in a lifespan startup event. You instantiate client = TestClient(app) without with and call client.get("/data"). What happens?
- a.The endpoint fails or returns wrong data because startup never ran.Correct
- b.The endpoint works correctly since TestClient automatically triggers lifespan on the first request.
- c.The test raises a RuntimeError because lifespan requires a context manager.
- d.The endpoint returns stale data left over from a previous test run.
Why? this is the answer
Without the with statement, TestClient skips lifespan entirely, so startup state is never created and the endpoint behaves incorrectly. Distractor B is wrong because TestClient does not auto-trigger lifespan on the first request; it only runs when used as a context manager.
Just read this? Test yourself on what you have been reading.
Read the original → fastapi.tiangolo.com
Put your scrolling time to good use
Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Open roles that interview on fastapi — each one lists the topics its interview covers.
See open roles