[{"data":1,"prerenderedAt":1819},["ShallowReactive",2],{"nav":3,"page-\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftesting-async-endpoints-with-pytest-asyncio\u002F":580,"surround-\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftesting-async-endpoints-with-pytest-asyncio\u002F":1818},[4,186,386],{"title":5,"path":6,"stem":7,"children":8},"Advanced Pydantic Validation Serialization","\u002Fadvanced-pydantic-validation-serialization","advanced-pydantic-validation-serialization",[9,12,42,66,90,114,150,174],{"title":10,"path":6,"stem":11},"Advanced Pydantic Validation and Serialization","advanced-pydantic-validation-serialization\u002Findex",{"title":13,"path":14,"stem":15,"children":16},"Custom Validators and Field Constraints in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Findex",[17,18,24,30,36],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":22},"Before, After and Wrap Validators in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fbefore-after-and-wrap-validators","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fbefore-after-and-wrap-validators\u002Findex",[23],{"title":19,"path":20,"stem":21},{"title":25,"path":26,"stem":27,"children":28},"Creating Reusable Custom Validators in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic\u002Findex",[29],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":34},"Cross-Field Validation Patterns in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcross-field-validation-patterns","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcross-field-validation-patterns\u002Findex",[35],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":40},"Pydantic v2 Async Custom Validator: What to Do Instead","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator\u002Findex",[41],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":46},"JSON Schema Customization in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Findex",[47,48,54,60],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":52},"Customizing OpenAPI Schema Generation in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi\u002Findex",[53],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":58},"Discriminated Unions in OpenAPI with Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fdiscriminated-unions-in-openapi","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fdiscriminated-unions-in-openapi\u002Findex",[59],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":64},"Examples in the OpenAPI Schema with FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fexamples-in-openapi-schema","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fexamples-in-openapi-schema\u002Findex",[65],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":70},"Nested Model Serialization in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Findex",[71,72,78,84],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":76},"Excluding Fields Per Endpoint in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fexcluding-fields-per-endpoint","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fexcluding-fields-per-endpoint\u002Findex",[77],{"title":73,"path":74,"stem":75},{"title":79,"path":80,"stem":81,"children":82},"Handling Deeply Nested JSON Models Efficiently","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently\u002Findex",[83],{"title":79,"path":80,"stem":81},{"title":85,"path":86,"stem":87,"children":88},"Self-Referencing and Recursive Models in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fself-referencing-and-recursive-models","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fself-referencing-and-recursive-models\u002Findex",[89],{"title":85,"path":86,"stem":87},{"title":91,"path":92,"stem":93,"children":94},"Performance Optimization for Pydantic Models in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Findex",[95,96,102,108],{"title":91,"path":92,"stem":93},{"title":97,"path":98,"stem":99,"children":100},"model_construct and When to Skip Validation in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fmodel-construct-when-to-skip-validation","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fmodel-construct-when-to-skip-validation\u002Findex",[101],{"title":97,"path":98,"stem":99},{"title":103,"path":104,"stem":105,"children":106},"Pydantic Model Serialization Performance in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance\u002Findex",[107],{"title":103,"path":104,"stem":105},{"title":109,"path":110,"stem":111,"children":112},"TypeAdapter for Non-Model Types in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Ftypeadapter-for-non-model-types","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Ftypeadapter-for-non-model-types\u002Findex",[113],{"title":109,"path":110,"stem":111},{"title":115,"path":116,"stem":117,"children":118},"Pydantic V2 Migration Guide for FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Findex",[119,120,126,132,138,144],{"title":115,"path":116,"stem":117},{"title":121,"path":122,"stem":123,"children":124},"Migrate @validator to @field_validator in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator\u002Findex",[125],{"title":121,"path":122,"stem":123},{"title":127,"path":128,"stem":129,"children":130},"Migrating from Pydantic v1 to v2 Without Breaking APIs","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrating-from-pydantic-v1-to-v2-without-breaking-apis","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrating-from-pydantic-v1-to-v2-without-breaking-apis\u002Findex",[131],{"title":127,"path":128,"stem":129},{"title":133,"path":134,"stem":135,"children":136},"model_config vs class Config in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config\u002Findex",[137],{"title":133,"path":134,"stem":135},{"title":139,"path":140,"stem":141,"children":142},"Replacing json_encoders with field_serializer in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Freplacing-json-encoders-with-field-serializer","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Freplacing-json-encoders-with-field-serializer\u002Findex",[143],{"title":139,"path":140,"stem":141},{"title":145,"path":146,"stem":147,"children":148},"Migrating @root_validator to @model_validator in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Froot-validator-to-model-validator","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Froot-validator-to-model-validator\u002Findex",[149],{"title":145,"path":146,"stem":147},{"title":151,"path":152,"stem":153,"children":154},"Request Validation Patterns in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Findex",[155,156,162,168],{"title":151,"path":152,"stem":153},{"title":157,"path":158,"stem":159,"children":160},"Optional vs Nullable Fields in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Foptional-vs-nullable-fields","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Foptional-vs-nullable-fields\u002Findex",[161],{"title":157,"path":158,"stem":159},{"title":163,"path":164,"stem":165,"children":166},"Query, Path and Body Parameter Validation in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fquery-path-and-body-parameter-validation","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fquery-path-and-body-parameter-validation\u002Findex",[167],{"title":163,"path":164,"stem":165},{"title":169,"path":170,"stem":171,"children":172},"Validating File Uploads and Forms in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fvalidating-file-uploads-and-forms","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fvalidating-file-uploads-and-forms\u002Findex",[173],{"title":169,"path":170,"stem":171},{"title":175,"path":176,"stem":177,"children":178},"Type Hinting and IDE Integration in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Findex",[179,180],{"title":175,"path":176,"stem":177},{"title":181,"path":182,"stem":183,"children":184},"Annotated Dependencies and Reusable Types in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Fannotated-dependencies-and-reusable-types","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Fannotated-dependencies-and-reusable-types\u002Findex",[185],{"title":181,"path":182,"stem":183},{"title":187,"path":188,"stem":189,"children":190},"Async Background Tasks Observability","\u002Fasync-background-tasks-observability","async-background-tasks-observability",[191,194,224,254,284,308,338,362],{"title":192,"path":188,"stem":193},"Async, Background Tasks, and Observability in FastAPI","async-background-tasks-observability\u002Findex",{"title":195,"path":196,"stem":197,"children":198},"Async Correctness and Concurrency in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Findex",[199,200,206,212,218],{"title":195,"path":196,"stem":197},{"title":201,"path":202,"stem":203,"children":204},"Concurrent Requests with asyncio.gather in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Fconcurrent-requests-with-asyncio-gather","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Fconcurrent-requests-with-asyncio-gather\u002Findex",[205],{"title":201,"path":202,"stem":203},{"title":207,"path":208,"stem":209,"children":210},"FastAPI async def vs def: Performance and When to Use Each","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance\u002Findex",[211],{"title":207,"path":208,"stem":209},{"title":213,"path":214,"stem":215,"children":216},"Fixing Blocking Calls in Async FastAPI Routes","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes\u002Findex",[217],{"title":213,"path":214,"stem":215},{"title":219,"path":220,"stem":221,"children":222},"Running Sync Code in a Threadpool in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Frunning-sync-code-in-a-threadpool","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Frunning-sync-code-in-a-threadpool\u002Findex",[223],{"title":219,"path":220,"stem":221},{"title":225,"path":226,"stem":227,"children":228},"Async Database Sessions in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions","async-background-tasks-observability\u002Fasync-database-sessions\u002Findex",[229,230,236,242,248],{"title":225,"path":226,"stem":227},{"title":231,"path":232,"stem":233,"children":234},"Async SQLAlchemy Session per Request in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request","async-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request\u002Findex",[235],{"title":231,"path":232,"stem":233},{"title":237,"path":238,"stem":239,"children":240},"Fixing asyncpg Connection Pool Exhaustion in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion","async-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion\u002Findex",[241],{"title":237,"path":238,"stem":239},{"title":243,"path":244,"stem":245,"children":246},"Testing with Async Database Fixtures in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ftesting-with-async-database-fixtures","async-background-tasks-observability\u002Fasync-database-sessions\u002Ftesting-with-async-database-fixtures\u002Findex",[247],{"title":243,"path":244,"stem":245},{"title":249,"path":250,"stem":251,"children":252},"Transaction Management and Rollback in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ftransaction-management-and-rollback","async-background-tasks-observability\u002Fasync-database-sessions\u002Ftransaction-management-and-rollback\u002Findex",[253],{"title":249,"path":250,"stem":251},{"title":255,"path":256,"stem":257,"children":258},"Background Task Processing in FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing","async-background-tasks-observability\u002Fbackground-task-processing\u002Findex",[259,260,266,272,278],{"title":255,"path":256,"stem":257},{"title":261,"path":262,"stem":263,"children":264},"FastAPI BackgroundTasks vs Celery vs ARQ","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq","async-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq\u002Findex",[265],{"title":261,"path":262,"stem":263},{"title":267,"path":268,"stem":269,"children":270},"Retry and Idempotency for FastAPI Background Tasks","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Fretry-and-idempotency-for-tasks","async-background-tasks-observability\u002Fbackground-task-processing\u002Fretry-and-idempotency-for-tasks\u002Findex",[271],{"title":267,"path":268,"stem":269},{"title":273,"path":274,"stem":275,"children":276},"Running ARQ Workers with FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi","async-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi\u002Findex",[277],{"title":273,"path":274,"stem":275},{"title":279,"path":280,"stem":281,"children":282},"When FastAPI BackgroundTasks Silently Fails","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Fwhen-backgroundtasks-silently-fails","async-background-tasks-observability\u002Fbackground-task-processing\u002Fwhen-backgroundtasks-silently-fails\u002Findex",[283],{"title":279,"path":280,"stem":281},{"title":285,"path":286,"stem":287,"children":288},"Caching Strategies in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies","async-background-tasks-observability\u002Fcaching-strategies\u002Findex",[289,290,296,302],{"title":285,"path":286,"stem":287},{"title":291,"path":292,"stem":293,"children":294},"Cache Invalidation Patterns in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi\u002Findex",[295],{"title":291,"path":292,"stem":293},{"title":297,"path":298,"stem":299,"children":300},"Caching Dependency Results in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fcaching-dependency-results","async-background-tasks-observability\u002Fcaching-strategies\u002Fcaching-dependency-results\u002Findex",[301],{"title":297,"path":298,"stem":299},{"title":303,"path":304,"stem":305,"children":306},"Redis Response Caching in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi\u002Findex",[307],{"title":303,"path":304,"stem":305},{"title":309,"path":310,"stem":311,"children":312},"Observability and Tracing in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing","async-background-tasks-observability\u002Fobservability-and-tracing\u002Findex",[313,314,320,326,332],{"title":309,"path":310,"stem":311},{"title":315,"path":316,"stem":317,"children":318},"Correlating Logs, Traces and Errors in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fcorrelating-logs-traces-and-errors","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fcorrelating-logs-traces-and-errors\u002Findex",[319],{"title":315,"path":316,"stem":317},{"title":321,"path":322,"stem":323,"children":324},"Instrumenting FastAPI with OpenTelemetry","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry","async-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry\u002Findex",[325],{"title":321,"path":322,"stem":323},{"title":327,"path":328,"stem":329,"children":330},"Prometheus Metrics for FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fprometheus-metrics-for-fastapi","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fprometheus-metrics-for-fastapi\u002Findex",[331],{"title":327,"path":328,"stem":329},{"title":333,"path":334,"stem":335,"children":336},"Structured JSON Logging with Request IDs in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids\u002Findex",[337],{"title":333,"path":334,"stem":335},{"title":339,"path":340,"stem":341,"children":342},"Rate Limiting and Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Findex",[343,344,350,356],{"title":339,"path":340,"stem":341},{"title":345,"path":346,"stem":347,"children":348},"FastAPI Rate Limiting with Redis and SlowAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi","async-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi\u002Findex",[349],{"title":345,"path":346,"stem":347},{"title":351,"path":352,"stem":353,"children":354},"Per-User Token Bucket Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling\u002Findex",[355],{"title":351,"path":352,"stem":353},{"title":357,"path":358,"stem":359,"children":360},"Rate Limit Headers and 429 Responses in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Frate-limit-headers-and-429-responses","async-background-tasks-observability\u002Frate-limiting-throttling\u002Frate-limit-headers-and-429-responses\u002Findex",[361],{"title":357,"path":358,"stem":359},{"title":363,"path":364,"stem":365,"children":366},"Testing FastAPI Applications","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Findex",[367,368,374,380],{"title":363,"path":364,"stem":365},{"title":369,"path":370,"stem":371,"children":372},"Mocking External Services in FastAPI Tests","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Fmocking-external-services-in-tests","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Fmocking-external-services-in-tests\u002Findex",[373],{"title":369,"path":370,"stem":371},{"title":375,"path":376,"stem":377,"children":378},"TestClient vs httpx AsyncClient in FastAPI","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftestclient-vs-httpx-asyncclient","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftestclient-vs-httpx-asyncclient\u002Findex",[379],{"title":375,"path":376,"stem":377},{"title":381,"path":382,"stem":383,"children":384},"Testing Async FastAPI Endpoints with pytest-asyncio","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftesting-async-endpoints-with-pytest-asyncio","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftesting-async-endpoints-with-pytest-asyncio\u002Findex",[385],{"title":381,"path":382,"stem":383},{"title":387,"path":388,"stem":389,"children":390},"Core Architecture Routing Patterns","\u002Fcore-architecture-routing-patterns","core-architecture-routing-patterns",[391,394,412,436,472,496,526,556],{"title":392,"path":388,"stem":393},"FastAPI Core Architecture and Routing Patterns","core-architecture-routing-patterns\u002Findex",{"title":395,"path":396,"stem":397,"children":398},"Application Factory Patterns in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Findex",[399,400,406],{"title":395,"path":396,"stem":397},{"title":401,"path":402,"stem":403,"children":404},"FastAPI App Factory Pattern for Testing and Deployment","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment\u002Findex",[405],{"title":401,"path":402,"stem":403},{"title":407,"path":408,"stem":409,"children":410},"Lifespan Events vs Startup and Shutdown in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Flifespan-events-vs-startup-shutdown","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Flifespan-events-vs-startup-shutdown\u002Findex",[411],{"title":407,"path":408,"stem":409},{"title":413,"path":414,"stem":415,"children":416},"Configuration Management in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management","core-architecture-routing-patterns\u002Fconfiguration-management\u002Findex",[417,418,424,430],{"title":413,"path":414,"stem":415},{"title":419,"path":420,"stem":421,"children":422},"Managing Environment Variables with Pydantic Settings","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings\u002Findex",[423],{"title":419,"path":420,"stem":421},{"title":425,"path":426,"stem":427,"children":428},"Pydantic Settings vs Dynaconf vs python-decouple","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple\u002Findex",[429],{"title":425,"path":426,"stem":427},{"title":431,"path":432,"stem":433,"children":434},"Secrets and .env Files Per Environment in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fsecrets-and-env-files-per-environment","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fsecrets-and-env-files-per-environment\u002Findex",[435],{"title":431,"path":432,"stem":433},{"title":437,"path":438,"stem":439,"children":440},"Dependency Injection Strategies in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Findex",[441,442,448,454,460,466],{"title":437,"path":438,"stem":439},{"title":443,"path":444,"stem":445,"children":446},"Best Practices for FastAPI Dependency Injection","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection\u002Findex",[447],{"title":443,"path":444,"stem":445},{"title":449,"path":450,"stem":451,"children":452},"Dependency Caching and use_cache in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fdependency-caching-and-use-cache","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fdependency-caching-and-use-cache\u002Findex",[453],{"title":449,"path":450,"stem":451},{"title":455,"path":456,"stem":457,"children":458},"Fixing FastAPI Dependency Injection Circular Imports","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix\u002Findex",[459],{"title":455,"path":456,"stem":457},{"title":461,"path":462,"stem":463,"children":464},"Overriding Dependencies in FastAPI Tests","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Foverriding-dependencies-in-tests","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Foverriding-dependencies-in-tests\u002Findex",[465],{"title":461,"path":462,"stem":463},{"title":467,"path":468,"stem":469,"children":470},"Yield Dependencies and Cleanup Order in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order\u002Findex",[471],{"title":467,"path":468,"stem":469},{"title":473,"path":474,"stem":475,"children":476},"Error Handling and Global Exceptions in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Findex",[477,478,484,490],{"title":473,"path":474,"stem":475},{"title":479,"path":480,"stem":481,"children":482},"Customising Validation Error Responses in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fcustomising-validation-error-responses","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fcustomising-validation-error-responses\u002Findex",[483],{"title":479,"path":480,"stem":481},{"title":485,"path":486,"stem":487,"children":488},"Global Exception Handlers for Consistent API Responses","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses\u002Findex",[489],{"title":485,"path":486,"stem":487},{"title":491,"path":492,"stem":493,"children":494},"HTTPException vs Custom Exception Classes in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fhttpexception-vs-custom-exception-classes","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fhttpexception-vs-custom-exception-classes\u002Findex",[495],{"title":491,"path":492,"stem":493},{"title":497,"path":498,"stem":499,"children":500},"Middleware Implementation in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Findex",[501,502,508,514,520],{"title":497,"path":498,"stem":499},{"title":503,"path":504,"stem":505,"children":506},"CORS Middleware Configuration in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration\u002Findex",[507],{"title":503,"path":504,"stem":505},{"title":509,"path":510,"stem":511,"children":512},"Implementing Custom Middleware for Request Tracing","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing\u002Findex",[513],{"title":509,"path":510,"stem":511},{"title":515,"path":516,"stem":517,"children":518},"Middleware Execution Order in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order\u002Findex",[519],{"title":515,"path":516,"stem":517},{"title":521,"path":522,"stem":523,"children":524},"Middleware vs Dependencies: When to Use Which","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which\u002Findex",[525],{"title":521,"path":522,"stem":523},{"title":527,"path":528,"stem":529,"children":530},"Modular Router Organization in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Findex",[531,532,538,544,550],{"title":527,"path":528,"stem":529},{"title":533,"path":534,"stem":535,"children":536},"APIRouter Prefix vs Sub-Application Mounting in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting\u002Findex",[537],{"title":533,"path":534,"stem":535},{"title":539,"path":540,"stem":541,"children":542},"How to Structure Large FastAPI Projects for Scale","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fhow-to-structure-large-fastapi-projects-for-scale","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fhow-to-structure-large-fastapi-projects-for-scale\u002Findex",[543],{"title":539,"path":540,"stem":541},{"title":545,"path":546,"stem":547,"children":548},"Router Tags and OpenAPI Grouping in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Frouter-tags-and-openapi-grouping","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Frouter-tags-and-openapi-grouping\u002Findex",[549],{"title":545,"path":546,"stem":547},{"title":551,"path":552,"stem":553,"children":554},"Versioning APIs with FastAPI Routers","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fversioning-apis-with-routers","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fversioning-apis-with-routers\u002Findex",[555],{"title":551,"path":552,"stem":553},{"title":557,"path":558,"stem":559,"children":560},"The FastAPI Request\u002FResponse Lifecycle","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Findex",[561,562,568,574],{"title":557,"path":558,"stem":559},{"title":563,"path":564,"stem":565,"children":566},"How a Request Flows Through FastAPI","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fhow-a-request-flows-through-fastapi","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fhow-a-request-flows-through-fastapi\u002Findex",[567],{"title":563,"path":564,"stem":565},{"title":569,"path":570,"stem":571,"children":572},"Response Model and Serialization Order","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fresponse-model-and-serialization-order","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fresponse-model-and-serialization-order\u002Findex",[573],{"title":569,"path":570,"stem":571},{"title":575,"path":576,"stem":577,"children":578},"Streaming and File Responses in FastAPI","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fstreaming-and-file-responses","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fstreaming-and-file-responses\u002Findex",[579],{"title":575,"path":576,"stem":577},{"id":581,"title":381,"body":582,"dateModified":1787,"datePublished":1787,"description":1788,"extension":1789,"faq":1790,"howto":1801,"meta":1802,"navigation":923,"path":382,"seo":1815,"stem":383,"type":1816,"__hash__":1817},"content\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftesting-async-endpoints-with-pytest-asyncio\u002Findex.md",{"type":583,"value":584,"toc":1776},"minimark",[585,589,596,636,648,766,771,782,796,805,840,843,847,865,880,895,899,902,1044,1051,1064,1067,1074,1083,1090,1094,1097,1117,1123,1429,1432,1438,1449,1452,1548,1555,1559,1568,1584,1606,1610,1613,1654,1665,1672,1676,1679,1686,1692,1696,1702,1708,1714,1720,1726,1730,1772],[586,587,381],"h1",{"id":588},"testing-async-fastapi-endpoints-with-pytest-asyncio",[590,591,592],"p",{},[593,594,595],"strong",{},"Key takeaways:",[597,598,599,611,619,622,625],"ul",{},[600,601,602,606,607,610],"li",{},[603,604,605],"code",{},"asyncio_mode = auto"," removes the per-test marker; ",[603,608,609],{},"strict"," is safer when other async plugins are installed.",[600,612,613,614,618],{},"Fixture scope and ",[615,616,617],"em",{},"loop"," scope are separate settings, and mismatching them is the single biggest source of confusion.",[600,620,621],{},"\"got Future attached to a different loop\" means a resource crossed loops — almost always a session fixture used by function-loop tests.",[600,623,624],{},"Match the loop scope of a shared resource to the loop scope of the tests that consume it.",[600,626,627,628,631,632,635],{},"Use ",[603,629,630],{},"httpx.AsyncClient"," with ",[603,633,634],{},"ASGITransport"," so the app runs on the test's loop.",[590,637,638,639,642,643,647],{},"Your async tests passed individually and then one of them started failing with a ",[603,640,641],{},"RuntimeError"," about event loops. This page is part of ",[644,645,363],"a",{"href":646},"\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002F",", and it covers the configuration that makes async tests work, plus the loop-scope error that everybody hits once. All output below is produced by running pytest 9.1.1 with pytest-asyncio 1.4.0 on Python 3.12.",[649,650,651,762],"figure",{},[652,653,661,662,661,666,661,670,661,679,661,686,661,693,661,698,661,702,661,708,661,711,661,714,661,718,661,723,661,726,661,730,661,734,661,737,661,739,661,742,661,746,661,749,661,753,661,756,661,759],"svg",{"viewBox":654,"role":655,"ariaLabelledBy":656,"xmlns":659,"style":660},"0 0 720 300","img",[657,658],"loop-title","loop-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;max-width:720px;margin:2rem 0","\n  ",[663,664,665],"title",{"id":657},"Loop scope mismatch versus matched loop scope",[667,668,669],"desc",{"id":658},"On the left a session fixture on the session loop is used by tests on separate function loops, which fails. On the right the fixture and both tests share the session loop and pass.",[671,672],"rect",{"x":673,"y":674,"width":675,"height":676,"rx":677,"style":678},"20","24","320","252","10","fill:none;stroke:currentColor;stroke-width:1.4px",[680,681,685],"text",{"x":682,"y":683,"style":684},"180","50","text-anchor:middle;fill:currentColor;font:600 14px sans-serif","mismatched",[671,687],{"x":688,"y":689,"width":690,"height":688,"rx":691,"style":692},"44","66","272","7","fill:#00796B;stroke:#00796B;stroke-width:1.4px",[680,694,697],{"x":682,"y":695,"style":696},"93","text-anchor:middle;fill:#ffffff;font:600 13px sans-serif","fixture on loop A",[671,699],{"x":688,"y":700,"width":701,"height":688,"rx":691,"style":678},"130","128",[680,703,707],{"x":704,"y":705,"style":706},"108","157","text-anchor:middle;fill:currentColor;font:600 13px sans-serif","test 1: loop B",[671,709],{"x":710,"y":700,"width":701,"height":688,"rx":691,"style":678},"188",[680,712,713],{"x":676,"y":705,"style":706},"test 2: loop C",[680,715,717],{"x":682,"y":716,"style":706},"212","RuntimeError:",[680,719,722],{"x":682,"y":720,"style":721},"232","text-anchor:middle;fill:currentColor;font:400 12px sans-serif","Future attached to",[680,724,725],{"x":682,"y":676,"style":721},"a different loop",[671,727],{"x":728,"y":674,"width":675,"height":676,"rx":677,"style":729},"380","fill:none;stroke:#00796B;stroke-width:1.8px",[680,731,733],{"x":732,"y":683,"style":684},"540","matched",[671,735],{"x":736,"y":689,"width":690,"height":688,"rx":691,"style":692},"404",[680,738,697],{"x":732,"y":695,"style":696},[671,740],{"x":736,"y":700,"width":701,"height":688,"rx":691,"style":741},"fill:none;stroke:#00796B;stroke-width:1.4px",[680,743,745],{"x":744,"y":705,"style":706},"468","test 1: loop A",[671,747],{"x":748,"y":700,"width":701,"height":688,"rx":691,"style":741},"548",[680,750,752],{"x":751,"y":705,"style":706},"612","test 2: loop A",[680,754,755],{"x":732,"y":716,"style":706},"2 passed",[680,757,758],{"x":732,"y":720,"style":721},"one loop owns the",[680,760,761],{"x":732,"y":676,"style":721},"shared resource",[763,764,765],"figcaption",{},"The fixture's loop and the test's loop must be the same loop for any resource that binds to one.",[767,768,770],"h2",{"id":769},"the-configuration","The Configuration",[590,772,773,774,777,778,781],{},"pytest cannot run a coroutine on its own — an ",[603,775,776],{},"async def"," test function returns a coroutine object that a plain pytest run would collect, never await, and (in recent versions) warn about. pytest-asyncio supplies the plumbing, and ",[603,779,780],{},"asyncio_mode"," decides how much you have to say about it.",[590,783,784,785,787,788,791,792,795],{},"In ",[593,786,609],{}," mode, the default, every async test needs ",[603,789,790],{},"@pytest.mark.asyncio"," and every async fixture needs ",[603,793,794],{},"@pytest_asyncio.fixture",". Verbose, but explicit, and it coexists safely with other async plugins such as anyio's, which is why library authors tend to prefer it.",[590,797,784,798,801,802,804],{},[593,799,800],{},"auto"," mode, every ",[603,803,776],{}," test is treated as an asyncio test and every async fixture is detected automatically. For an application codebase where everything is async, this is what you want:",[806,807,812],"pre",{"className":808,"code":809,"language":810,"meta":811,"style":811},"language-ini shiki shiki-themes github-light-high-contrast","[pytest]\nasyncio_mode = auto\nasyncio_default_fixture_loop_scope = session\nasyncio_default_test_loop_scope = session\n","ini","",[603,813,814,822,828,834],{"__ignoreMap":811},[815,816,819],"span",{"class":817,"line":818},"line",1,[815,820,821],{},"[pytest]\n",[815,823,825],{"class":817,"line":824},2,[815,826,827],{},"asyncio_mode = auto\n",[815,829,831],{"class":817,"line":830},3,[815,832,833],{},"asyncio_default_fixture_loop_scope = session\n",[815,835,837],{"class":817,"line":836},4,[815,838,839],{},"asyncio_default_test_loop_scope = session\n",[590,841,842],{},"Those last two lines are the part people omit, and they are the subject of the rest of this page.",[767,844,846],{"id":845},"scope-is-not-loop-scope","Scope Is Not Loop Scope",[590,848,849,850,853,854,857,858,853,861,864],{},"There are two independent questions about an async fixture. ",[615,851,852],{},"How often is it created?"," — that is ",[603,855,856],{},"scope",", the ordinary pytest concept. ",[615,859,860],{},"Which event loop does it run on?",[603,862,863],{},"loop_scope",", which pytest-asyncio added precisely because the two answers are often different.",[590,866,867,868,871,872,875,876,879],{},"By default, tests run on a ",[593,869,870],{},"function-scoped"," event loop: a fresh loop per test, torn down after. That is a good default because it guarantees isolation. But a fixture declared ",[603,873,874],{},"scope=\"session\""," is created once and reused, while each consuming test brings its own loop — so any object the fixture bound to ",[615,877,878],{},"its"," loop is now being used from a different one.",[590,881,882,883,886,887,890,891,894],{},"Most async resources bind to a loop at creation or first use: ",[603,884,885],{},"asyncio.Future",", ",[603,888,889],{},"asyncio.Lock"," and ",[603,892,893],{},"asyncio.Queue"," when first awaited, database connection pools, and clients that maintain persistent connections. Awaiting such an object from a different loop is what produces the error below.",[767,896,898],{"id":897},"the-real-failure","The Real Failure",[590,900,901],{},"Here is the smallest reproduction — a session-scoped fixture creating a future, and a test awaiting it:",[806,903,907],{"className":904,"code":905,"language":906,"meta":811,"style":811},"language-python shiki shiki-themes github-light-high-contrast","import asyncio\n\nimport pytest\n\n\n@pytest.fixture(scope=\"session\")\nasync def pool():\n    \"\"\"A session-scoped async fixture: this future is bound to the loop that ran the fixture.\"\"\"\n    return asyncio.get_running_loop().create_future()\n\n\nasync def test_uses_pool(pool):\n    # Each test gets its own event loop by default, so awaiting the fixture's future explodes.\n    await asyncio.wait_for(pool, timeout=0.05)\n","python",[603,908,909,919,925,932,936,941,964,979,985,994,999,1004,1017,1024],{"__ignoreMap":811},[815,910,911,915],{"class":817,"line":818},[815,912,914],{"class":913},"sTJeM","import",[815,916,918],{"class":917},"sigWx"," asyncio\n",[815,920,921],{"class":817,"line":824},[815,922,924],{"emptyLinePlaceholder":923},true,"\n",[815,926,927,929],{"class":817,"line":830},[815,928,914],{"class":913},[815,930,931],{"class":917}," pytest\n",[815,933,934],{"class":817,"line":836},[815,935,924],{"emptyLinePlaceholder":923},[815,937,939],{"class":817,"line":938},5,[815,940,924],{"emptyLinePlaceholder":923},[815,942,944,948,951,954,957,961],{"class":817,"line":943},6,[815,945,947],{"class":946},"s3dhs","@pytest.fixture",[815,949,950],{"class":917},"(",[815,952,856],{"class":953},"sV4o_",[815,955,956],{"class":913},"=",[815,958,960],{"class":959},"sYEJz","\"session\"",[815,962,963],{"class":917},")\n",[815,965,967,970,973,976],{"class":817,"line":966},7,[815,968,969],{"class":913},"async",[815,971,972],{"class":913}," def",[815,974,975],{"class":946}," pool",[815,977,978],{"class":917},"():\n",[815,980,982],{"class":817,"line":981},8,[815,983,984],{"class":959},"    \"\"\"A session-scoped async fixture: this future is bound to the loop that ran the fixture.\"\"\"\n",[815,986,988,991],{"class":817,"line":987},9,[815,989,990],{"class":913},"    return",[815,992,993],{"class":917}," asyncio.get_running_loop().create_future()\n",[815,995,997],{"class":817,"line":996},10,[815,998,924],{"emptyLinePlaceholder":923},[815,1000,1002],{"class":817,"line":1001},11,[815,1003,924],{"emptyLinePlaceholder":923},[815,1005,1007,1009,1011,1014],{"class":817,"line":1006},12,[815,1008,969],{"class":913},[815,1010,972],{"class":913},[815,1012,1013],{"class":946}," test_uses_pool",[815,1015,1016],{"class":917},"(pool):\n",[815,1018,1020],{"class":817,"line":1019},13,[815,1021,1023],{"class":1022},"sFeEa","    # Each test gets its own event loop by default, so awaiting the fixture's future explodes.\n",[815,1025,1027,1030,1033,1036,1038,1042],{"class":817,"line":1026},14,[815,1028,1029],{"class":913},"    await",[815,1031,1032],{"class":917}," asyncio.wait_for(pool, ",[815,1034,1035],{"class":953},"timeout",[815,1037,956],{"class":913},[815,1039,1041],{"class":1040},"sacAq","0.05",[815,1043,963],{"class":917},[590,1045,1046,1047,1050],{},"with ",[603,1048,1049],{},"pytest.ini",":",[806,1052,1054],{"className":808,"code":1053,"language":810,"meta":811,"style":811},"[pytest]\nasyncio_mode = auto\n",[603,1055,1056,1060],{"__ignoreMap":811},[815,1057,1058],{"class":817,"line":818},[815,1059,821],{},[815,1061,1062],{"class":817,"line":824},[815,1063,827],{},[590,1065,1066],{},"Running that under pytest produces this — real output, captured by executing pytest as a subprocess and normalising only the temporary directory paths and the run duration:",[806,1068,1072],{"className":1069,"code":1071,"language":680,"meta":811},[1070],"language-text","$ GET \u002Fbroken\n200 OK\n{\n  \"exit_code\": 1,\n  \"output\": [\n    \"F                                                                        [100%]\",\n    \"=================================== FAILURES ===================================\",\n    \"E   RuntimeError: Task \u003CTask pending name='Task-2' coro=\u003Ctest_uses_pool() running at \u002Fdemo\u002Ftest_case.py:15> cb=[_run_until_complete_cb() at \u003Cstdlib>\u002Fasyncio\u002Fbase_events.py:181]> got Future \u003CFuture pending> attached to a different loop\",\n    \"\u003Cstdlib>\u002Fasyncio\u002Ftasks.py:520: RuntimeError: Task \u003CTask pending name='Task-2' coro=\u003Ctest_uses_pool() running at \u002Fdemo\u002Ftest_case.py:15> cb=[_run_until_complete_cb() at \u003Cstdlib>\u002Fasyncio\u002Fbase_events.py:181]> got Future \u003CFuture pending> attached to a different loop\",\n    \"=========================== short test summary info ============================\",\n    \"FAILED test_case.py::test_uses_pool - RuntimeError: Task \u003CTask pending name='...\",\n    \"1 failed in Xs\"\n  ]\n}\n",[603,1073,1071],{"__ignoreMap":811},[590,1075,1076,1079,1080,1082],{},[603,1077,1078],{},"got Future \u003CFuture pending> attached to a different loop"," is the exact string, and it is worth recognising because the message names neither the fixture nor the scope setting that caused it. In a real suite this shows up as an SQLAlchemy async engine, an aioredis client, or an ",[603,1081,630],{}," created once for the session — the future in this example is just the smallest object with the same binding behaviour.",[590,1084,1085,1086,1089],{},"A close relative is ",[603,1087,1088],{},"RuntimeError: Event loop is closed",", which appears when the fixture's loop was torn down before the resource was cleaned up. Same root cause, different timing.",[767,1091,1093],{"id":1092},"the-fix","The Fix",[590,1095,1096],{},"Align the loop scopes. If a resource is shared across tests, the tests must run on the loop that created it:",[806,1098,1099],{"className":808,"code":809,"language":810,"meta":811,"style":811},[603,1100,1101,1105,1109,1113],{"__ignoreMap":811},[815,1102,1103],{"class":817,"line":818},[815,1104,821],{},[815,1106,1107],{"class":817,"line":824},[815,1108,827],{},[815,1110,1111],{"class":817,"line":830},[815,1112,833],{},[815,1114,1115],{"class":817,"line":836},[815,1116,839],{},[590,1118,1119,1120,1122],{},"With that configuration and the same fixture, plus an ",[603,1121,630],{}," bound to the app:",[806,1124,1126],{"className":904,"code":1125,"language":906,"meta":811,"style":811},"import asyncio\n\nimport httpx\nimport pytest\n\nfrom app_under_test import app\n\n\n@pytest.fixture(scope=\"session\")\nasync def pool():\n    return asyncio.get_running_loop().create_future()\n\n\n@pytest.fixture(scope=\"session\")\nasync def client():\n    transport = httpx.ASGITransport(app=app)\n    async with httpx.AsyncClient(transport=transport, base_url=\"http:\u002F\u002Ft\") as c:\n        yield c\n\n\nasync def test_same_loop(pool):\n    # Fixture and test now share the session loop, so the future is usable here.\n    assert asyncio.get_running_loop() is pool.get_loop()\n\n\nasync def test_endpoint(client):\n    response = await client.get(\"\u002Fhealth\")\n    assert response.status_code == 200\n    assert response.json() == {\"status\": \"ok\"}\n",[603,1127,1128,1134,1138,1145,1151,1155,1168,1172,1176,1190,1200,1206,1210,1214,1228,1240,1259,1296,1305,1310,1315,1327,1333,1348,1353,1358,1371,1390,1404],{"__ignoreMap":811},[815,1129,1130,1132],{"class":817,"line":818},[815,1131,914],{"class":913},[815,1133,918],{"class":917},[815,1135,1136],{"class":817,"line":824},[815,1137,924],{"emptyLinePlaceholder":923},[815,1139,1140,1142],{"class":817,"line":830},[815,1141,914],{"class":913},[815,1143,1144],{"class":917}," httpx\n",[815,1146,1147,1149],{"class":817,"line":836},[815,1148,914],{"class":913},[815,1150,931],{"class":917},[815,1152,1153],{"class":817,"line":938},[815,1154,924],{"emptyLinePlaceholder":923},[815,1156,1157,1160,1163,1165],{"class":817,"line":943},[815,1158,1159],{"class":913},"from",[815,1161,1162],{"class":917}," app_under_test ",[815,1164,914],{"class":913},[815,1166,1167],{"class":917}," app\n",[815,1169,1170],{"class":817,"line":966},[815,1171,924],{"emptyLinePlaceholder":923},[815,1173,1174],{"class":817,"line":981},[815,1175,924],{"emptyLinePlaceholder":923},[815,1177,1178,1180,1182,1184,1186,1188],{"class":817,"line":987},[815,1179,947],{"class":946},[815,1181,950],{"class":917},[815,1183,856],{"class":953},[815,1185,956],{"class":913},[815,1187,960],{"class":959},[815,1189,963],{"class":917},[815,1191,1192,1194,1196,1198],{"class":817,"line":996},[815,1193,969],{"class":913},[815,1195,972],{"class":913},[815,1197,975],{"class":946},[815,1199,978],{"class":917},[815,1201,1202,1204],{"class":817,"line":1001},[815,1203,990],{"class":913},[815,1205,993],{"class":917},[815,1207,1208],{"class":817,"line":1006},[815,1209,924],{"emptyLinePlaceholder":923},[815,1211,1212],{"class":817,"line":1019},[815,1213,924],{"emptyLinePlaceholder":923},[815,1215,1216,1218,1220,1222,1224,1226],{"class":817,"line":1026},[815,1217,947],{"class":946},[815,1219,950],{"class":917},[815,1221,856],{"class":953},[815,1223,956],{"class":913},[815,1225,960],{"class":959},[815,1227,963],{"class":917},[815,1229,1231,1233,1235,1238],{"class":817,"line":1230},15,[815,1232,969],{"class":913},[815,1234,972],{"class":913},[815,1236,1237],{"class":946}," client",[815,1239,978],{"class":917},[815,1241,1243,1246,1248,1251,1254,1256],{"class":817,"line":1242},16,[815,1244,1245],{"class":917},"    transport ",[815,1247,956],{"class":913},[815,1249,1250],{"class":917}," httpx.ASGITransport(",[815,1252,1253],{"class":953},"app",[815,1255,956],{"class":913},[815,1257,1258],{"class":917},"app)\n",[815,1260,1262,1265,1268,1271,1274,1276,1279,1282,1284,1287,1290,1293],{"class":817,"line":1261},17,[815,1263,1264],{"class":913},"    async",[815,1266,1267],{"class":913}," with",[815,1269,1270],{"class":917}," httpx.AsyncClient(",[815,1272,1273],{"class":953},"transport",[815,1275,956],{"class":913},[815,1277,1278],{"class":917},"transport, ",[815,1280,1281],{"class":953},"base_url",[815,1283,956],{"class":913},[815,1285,1286],{"class":959},"\"http:\u002F\u002Ft\"",[815,1288,1289],{"class":917},") ",[815,1291,1292],{"class":913},"as",[815,1294,1295],{"class":917}," c:\n",[815,1297,1299,1302],{"class":817,"line":1298},18,[815,1300,1301],{"class":913},"        yield",[815,1303,1304],{"class":917}," c\n",[815,1306,1308],{"class":817,"line":1307},19,[815,1309,924],{"emptyLinePlaceholder":923},[815,1311,1313],{"class":817,"line":1312},20,[815,1314,924],{"emptyLinePlaceholder":923},[815,1316,1318,1320,1322,1325],{"class":817,"line":1317},21,[815,1319,969],{"class":913},[815,1321,972],{"class":913},[815,1323,1324],{"class":946}," test_same_loop",[815,1326,1016],{"class":917},[815,1328,1330],{"class":817,"line":1329},22,[815,1331,1332],{"class":1022},"    # Fixture and test now share the session loop, so the future is usable here.\n",[815,1334,1336,1339,1342,1345],{"class":817,"line":1335},23,[815,1337,1338],{"class":913},"    assert",[815,1340,1341],{"class":917}," asyncio.get_running_loop() ",[815,1343,1344],{"class":913},"is",[815,1346,1347],{"class":917}," pool.get_loop()\n",[815,1349,1351],{"class":817,"line":1350},24,[815,1352,924],{"emptyLinePlaceholder":923},[815,1354,1356],{"class":817,"line":1355},25,[815,1357,924],{"emptyLinePlaceholder":923},[815,1359,1361,1363,1365,1368],{"class":817,"line":1360},26,[815,1362,969],{"class":913},[815,1364,972],{"class":913},[815,1366,1367],{"class":946}," test_endpoint",[815,1369,1370],{"class":917},"(client):\n",[815,1372,1374,1377,1379,1382,1385,1388],{"class":817,"line":1373},27,[815,1375,1376],{"class":917},"    response ",[815,1378,956],{"class":913},[815,1380,1381],{"class":913}," await",[815,1383,1384],{"class":917}," client.get(",[815,1386,1387],{"class":959},"\"\u002Fhealth\"",[815,1389,963],{"class":917},[815,1391,1393,1395,1398,1401],{"class":817,"line":1392},28,[815,1394,1338],{"class":913},[815,1396,1397],{"class":917}," response.status_code ",[815,1399,1400],{"class":913},"==",[815,1402,1403],{"class":1040}," 200\n",[815,1405,1407,1409,1412,1414,1417,1420,1423,1426],{"class":817,"line":1406},29,[815,1408,1338],{"class":913},[815,1410,1411],{"class":917}," response.json() ",[815,1413,1400],{"class":913},[815,1415,1416],{"class":917}," {",[815,1418,1419],{"class":959},"\"status\"",[815,1421,1422],{"class":917},": ",[815,1424,1425],{"class":959},"\"ok\"",[815,1427,1428],{"class":917},"}\n",[590,1430,1431],{},"The real result:",[806,1433,1436],{"className":1434,"code":1435,"language":680,"meta":811},[1070],"$ GET \u002Ffixed\n200 OK\n{\n  \"exit_code\": 0,\n  \"output\": [\n    \"..                                                                       [100%]\",\n    \"2 passed in Xs\"\n  ]\n}\n",[603,1437,1435],{"__ignoreMap":811},[590,1439,1440,1441,1444,1445,1448],{},"The ",[603,1442,1443],{},"test_same_loop"," assertion is doing real work: ",[603,1446,1447],{},"pool.get_loop()"," returns the loop the future is bound to, and comparing it by identity to the running loop is a direct statement of the invariant that was violated before.",[590,1450,1451],{},"If you prefer per-fixture control to a global setting — and in a suite with a mix of shared and per-test resources you probably do — set the scopes at the declaration:",[806,1453,1455],{"className":904,"code":1454,"language":906,"meta":811,"style":811},"import pytest_asyncio\n\n\n@pytest_asyncio.fixture(scope=\"session\", loop_scope=\"session\")\nasync def engine():\n    ...\n\n\n@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_uses_engine(engine):\n    ...\n",[603,1456,1457,1464,1468,1472,1494,1505,1510,1514,1518,1532,1544],{"__ignoreMap":811},[815,1458,1459,1461],{"class":817,"line":818},[815,1460,914],{"class":913},[815,1462,1463],{"class":917}," pytest_asyncio\n",[815,1465,1466],{"class":817,"line":824},[815,1467,924],{"emptyLinePlaceholder":923},[815,1469,1470],{"class":817,"line":830},[815,1471,924],{"emptyLinePlaceholder":923},[815,1473,1474,1476,1478,1480,1482,1484,1486,1488,1490,1492],{"class":817,"line":836},[815,1475,794],{"class":946},[815,1477,950],{"class":917},[815,1479,856],{"class":953},[815,1481,956],{"class":913},[815,1483,960],{"class":959},[815,1485,886],{"class":917},[815,1487,863],{"class":953},[815,1489,956],{"class":913},[815,1491,960],{"class":959},[815,1493,963],{"class":917},[815,1495,1496,1498,1500,1503],{"class":817,"line":938},[815,1497,969],{"class":913},[815,1499,972],{"class":913},[815,1501,1502],{"class":946}," engine",[815,1504,978],{"class":917},[815,1506,1507],{"class":817,"line":943},[815,1508,1509],{"class":1040},"    ...\n",[815,1511,1512],{"class":817,"line":966},[815,1513,924],{"emptyLinePlaceholder":923},[815,1515,1516],{"class":817,"line":981},[815,1517,924],{"emptyLinePlaceholder":923},[815,1519,1520,1522,1524,1526,1528,1530],{"class":817,"line":987},[815,1521,790],{"class":946},[815,1523,950],{"class":917},[815,1525,863],{"class":953},[815,1527,956],{"class":913},[815,1529,960],{"class":959},[815,1531,963],{"class":917},[815,1533,1534,1536,1538,1541],{"class":817,"line":996},[815,1535,969],{"class":913},[815,1537,972],{"class":913},[815,1539,1540],{"class":946}," test_uses_engine",[815,1542,1543],{"class":917},"(engine):\n",[815,1545,1546],{"class":817,"line":1001},[815,1547,1509],{"class":1040},[590,1549,1550,1551,1554],{},"The rule to internalise: ",[593,1552,1553],{},"a resource's loop scope must be at least as wide as the loop scope of every test that touches it."," A session-scoped resource with function-scoped tests is broken. A function-scoped resource with session-scoped tests is merely wasteful.",[767,1556,1558],{"id":1557},"why-asyncclient-rather-than-testclient","Why AsyncClient Rather Than TestClient",[590,1560,1561,1564,1565,1567],{},[603,1562,1563],{},"TestClient"," is synchronous. It runs your ASGI app on an event loop it manages in a worker thread through anyio's blocking portal, which means the app is not running on your test's loop. That is fine for straightforward request assertions and it is why ",[603,1566,1563],{}," works without any async configuration at all — but a session-scoped async fixture created on the test loop and a database session created on the portal's loop are once again two different loops.",[590,1569,1570,631,1572,1574,1575,1578,1579,1583],{},[603,1571,630],{},[603,1573,634],{}," runs the app in-process on the ",[615,1576,1577],{},"current"," loop, so fixtures, the client and the application share one loop and one context. That is what makes shared async resources behave, and it is why async-first suites standardise on it. The full comparison is in ",[644,1580,1582],{"href":1581},"\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftestclient-vs-httpx-asyncclient\u002F","TestClient vs httpx AsyncClient",".",[590,1585,1586,1587,1589,1590,1593,1594,1597,1598,1601,1602,1583],{},"One consequence worth knowing: with ",[603,1588,634],{}," there is no real socket, so the app's lifespan events do not run unless you drive them explicitly. If your startup handler is what populates ",[603,1591,1592],{},"app.state.db_pool",", tests will find it missing. Wrap the client fixture in ",[603,1595,1596],{},"LifespanManager"," from ",[603,1599,1600],{},"asgi-lifespan",", or set the state directly in the fixture — see ",[644,1603,1605],{"href":1604},"\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Flifespan-events-vs-startup-shutdown\u002F","lifespan events vs startup shutdown",[767,1607,1609],{"id":1608},"verification","Verification",[590,1611,1612],{},"Two habits catch loop problems before they become mysterious:",[806,1614,1616],{"className":904,"code":1615,"language":906,"meta":811,"style":811},"@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_fixture_and_test_share_a_loop(engine):\n    assert asyncio.get_running_loop() is engine.loop_at_creation\n",[603,1617,1618,1632,1643],{"__ignoreMap":811},[815,1619,1620,1622,1624,1626,1628,1630],{"class":817,"line":818},[815,1621,790],{"class":946},[815,1623,950],{"class":917},[815,1625,863],{"class":953},[815,1627,956],{"class":913},[815,1629,960],{"class":959},[815,1631,963],{"class":917},[815,1633,1634,1636,1638,1641],{"class":817,"line":824},[815,1635,969],{"class":913},[815,1637,972],{"class":913},[815,1639,1640],{"class":946}," test_fixture_and_test_share_a_loop",[815,1642,1543],{"class":917},[815,1644,1645,1647,1649,1651],{"class":817,"line":830},[815,1646,1338],{"class":913},[815,1648,1341],{"class":917},[815,1650,1344],{"class":913},[815,1652,1653],{"class":917}," engine.loop_at_creation\n",[590,1655,1656,1657,1660,1661,1664],{},"and running the suite in a different order (",[603,1658,1659],{},"pytest -p no:randomly"," off, or with ",[603,1662,1663],{},"pytest-randomly"," on) to make sure passing is not an accident of ordering. Loop-scope bugs are order-sensitive: the first test to touch a shared resource often succeeds and the second fails, so a suite that always runs alphabetically can hide one for months.",[590,1666,1667,1668,1671],{},"Also run with ",[603,1669,1670],{},"-W error::RuntimeWarning"," occasionally. \"coroutine was never awaited\" warnings usually mean a fixture or a call is being collected rather than executed, which is the strict-mode failure that quietly turns a test into a no-op.",[767,1673,1675],{"id":1674},"trade-offs-and-when-not-to","Trade-offs and When Not To",[590,1677,1678],{},"Session-scoped loops trade isolation for shared resources. Tests then share every loop-bound object, so a test that leaves a pending task, an unclosed connection or a mutated global affects the ones after it. If your suite is fast and your resources are cheap to build, function-scoped everything is simpler and safer — a fresh loop per test cannot leak state at all.",[590,1680,1681,1682,1583],{},"The middle ground most projects land on is a session-scoped engine or connection pool with a function-scoped transaction wrapped around each test, rolled back at the end. That gives you one expensive resource and per-test isolation of the data, and it is described in ",[644,1683,1685],{"href":1684},"\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ftesting-with-async-database-fixtures\u002F","testing with async database fixtures",[590,1687,1688,1689,1691],{},"Finally, ",[603,1690,605],{}," is convenient and slightly magical: every async test is claimed by pytest-asyncio, including tests you intended another plugin to run. In a repository where some tests use anyio's trio backend, stay in strict mode and mark explicitly. Convenience settings that silently claim ownership are the ones that cost you an afternoon later.",[767,1693,1695],{"id":1694},"faq","FAQ",[590,1697,1698,1701],{},[593,1699,1700],{},"What causes 'got Future attached to a different loop' in pytest-asyncio?","\nA resource created on one event loop is awaited on another. It usually means a session-scoped async fixture built a connection, lock or future on the session loop while each test runs on its own function-scoped loop.",[590,1703,1704,1707],{},[593,1705,1706],{},"What is the difference between fixture scope and loop scope?","\nScope controls how often the fixture value is created. Loop scope controls which event loop it runs on. They are independent in pytest-asyncio 1.x, so a session-scoped fixture can still execute on a function-scoped loop unless you set loop_scope explicitly.",[590,1709,1710,1713],{},[593,1711,1712],{},"Should I use asyncio_mode strict or auto?","\nAuto is less typing and treats every async test as an asyncio test, which suits a codebase where everything is async. Strict requires an explicit marker and is safer in a mixed codebase or when another async plugin is installed.",[590,1715,1716,1719],{},[593,1717,1718],{},"Do I still need TestClient if I use httpx AsyncClient?","\nNot for exercising async endpoints. AsyncClient with ASGITransport runs the app on the test's own loop, which is what makes async fixtures and shared resources work. TestClient runs the app on a loop it manages in a worker thread.",[590,1721,1722,1725],{},[593,1723,1724],{},"Why does my async fixture yield nothing usable?","\nUsually because it is declared with plain @pytest.fixture in strict mode, so pytest treats the async generator as the value. Use @pytest_asyncio.fixture, or switch to auto mode where async fixtures are detected for you.",[767,1727,1729],{"id":1728},"related-reading","Related Reading",[597,1731,1732,1740,1747,1756,1764],{},[600,1733,1734,1737,1738,1583],{},[593,1735,1736],{},"Up to the topic:"," ",[644,1739,363],{"href":646},[600,1741,1742,1737,1745,1583],{},[593,1743,1744],{},"Picking a client:",[644,1746,1582],{"href":1581},[600,1748,1749,1737,1752,1583],{},[593,1750,1751],{},"Replacing collaborators:",[644,1753,1755],{"href":1754},"\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Fmocking-external-services-in-tests\u002F","Mocking external services in tests",[600,1757,1758,1737,1761,1583],{},[593,1759,1760],{},"Database fixtures:",[644,1762,1763],{"href":1684},"Testing with async database fixtures",[600,1765,1766,1737,1769,1583],{},[593,1767,1768],{},"App startup in tests:",[644,1770,1771],{"href":1604},"Lifespan events vs startup shutdown",[1773,1774,1775],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sTJeM, html code.shiki .sTJeM{--shiki-default:#A0111F}html pre.shiki code .sigWx, html code.shiki .sigWx{--shiki-default:#0E1116}html pre.shiki code .s3dhs, html code.shiki .s3dhs{--shiki-default:#622CBC}html pre.shiki code .sV4o_, html code.shiki .sV4o_{--shiki-default:#702C00}html pre.shiki code .sYEJz, html code.shiki .sYEJz{--shiki-default:#032563}html pre.shiki code .sFeEa, html code.shiki .sFeEa{--shiki-default:#66707B}html pre.shiki code .sacAq, html code.shiki .sacAq{--shiki-default:#023B95}",{"title":811,"searchDepth":824,"depth":824,"links":1777},[1778,1779,1780,1781,1782,1783,1784,1785,1786],{"id":769,"depth":824,"text":770},{"id":845,"depth":824,"text":846},{"id":897,"depth":824,"text":898},{"id":1092,"depth":824,"text":1093},{"id":1557,"depth":824,"text":1558},{"id":1608,"depth":824,"text":1609},{"id":1674,"depth":824,"text":1675},{"id":1694,"depth":824,"text":1695},{"id":1728,"depth":824,"text":1729},"2026-07-20","Configure asyncio_mode, event loop scope and async fixtures for FastAPI tests, and fix the 'attached to a different loop' error with a real reproduction.","md",[1791,1793,1795,1797,1799],{"q":1700,"a":1792},"A resource created on one event loop is awaited on another. It usually means a session-scoped async fixture built a connection, lock or future on the session loop while each test runs on its own function-scoped loop.",{"q":1706,"a":1794},"Scope controls how often the fixture value is created. Loop scope controls which event loop it runs on. They are independent in pytest-asyncio 1.x, so a session-scoped fixture can still execute on a function-scoped loop unless you set loop_scope explicitly.",{"q":1712,"a":1796},"Auto is less typing and treats every async test as an asyncio test, which suits a codebase where everything is async. Strict requires an explicit marker and is safer in a mixed codebase or when another async plugin is installed.",{"q":1718,"a":1798},"Not for exercising async endpoints. AsyncClient with ASGITransport runs the app on the test's own loop, which is what makes async fixtures and shared resources work. TestClient runs the app on a loop it manages in a worker thread.",{"q":1724,"a":1800},"Usually because it is declared with plain @pytest.fixture in strict mode, so pytest treats the async generator as the value. Use @pytest_asyncio.fixture, or switch to auto mode where async fixtures are detected for you.",null,{"slug":1803,"breadcrumb":1804},"testing-async-endpoints-with-pytest-asyncio",[1805,1808,1811,1812],{"label":1806,"path":1807},"Home","\u002F",{"label":1809,"path":1810},"Async, Background Tasks & Observability","\u002Fasync-background-tasks-observability\u002F",{"label":363,"path":646},{"label":1813,"path":1814},"Testing Async Endpoints with pytest-asyncio","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftesting-async-endpoints-with-pytest-asyncio\u002F",{"title":381,"description":1788},"article","ZBgvDB3MzeZ8QNpJrXhp2DLuoECLxiG9J2U_WVSxZxs",[1801,1801],1784588203038]