Docker环境变量在FastAPI与pytest中未加载的问题求助
解决FastAPI测试中Docker环境变量不生效的问题
问题背景
搭建了包含PostgreSQL的FastAPI测试Docker环境,对多接口进行测试时,发现compose.yaml中配置的ENVIRONMENT=dev和TESTING=0环境变量未在pytest中生效,测试返回默认值dev2和True,导致断言失败。
测试文件代码
from fastapi.testclient import TestClient from app.main import app client = TestClient(app) def test_read_main(): response = client.get("/ping") assert response.status_code == 200 assert response.json() == {"ping": "pong!", "environment": "dev", "testing": 0}
报错信息
test_main.py F [100%] =========================== FAILURES =========================== ________________________ test_read_main ________________________ def test_read_main(): response = client.get("/ping") assert response.status_code == 200 > assert response.json() == {"ping": "pong!", "environment": "dev", "testing": 0} E AssertionError: assert {'environment...esting': True} == {'environment... 'testing': 0} E Omitting 1 identical items, use -vv to show E Differing items: E {'testing': True} != {'testing': 0} E {'environment': 'dev2'} != {'environment': 'dev'} E Use -v to get more diff test_main.py:11: AssertionError =================== short test summary info ==================== FAILED test_main.py::test_read_main - AssertionError: assert {'environment...esting': True} == {'... ====================== 1 failed in 0.44s =======================
项目核心代码
# services/backend/app/main.py from fastapi import FastAPI, Depends, HTTPException from config import get_settings, Settings app = FastAPI() @app.get("/ping") def pong(settings: Settings = Depends(get_settings)): return { "ping": "pong!", "environment": settings.environment, "testing": settings.testing, }
# backend/config.py import logging from functools import lru_cache from pydantic_settings import BaseSettings log = logging.getLogger("uvicorn") class Settings(BaseSettings): environment: str = "dev2" testing: bool = 1 @lru_cache def get_settings() -> BaseSettings: log.info("Loading config settings from environment...") return Settings()
项目结构与Docker配置
. ├── services │ ├── backend │ │ ├── alembic.ini │ │ ├── app │ │ │ ├── __init__.py │ │ │ ├── main.py │ │ │ ├── models.py │ │ │ ├── routers │ │ │ │ ├── admin.py │ │ │ │ ├── admin_schemas.py │ │ │ ├── schemas.py │ │ ├── config.py │ │ ├── db.py │ │ ├── Dockerfile │ │ ├── migrations │ │ ├── requirements.txt │ │ ├── setup.py │ │ └── test_main.py │ ├── compose.yaml │ └── frontend └── tree.txt
Dockerfile
# syntax=docker/dockerfile:1 ARG PYTHON_VERSION=3.11 FROM python:${PYTHON_VERSION}-slim as base ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONUNBUFFERED=1 WORKDIR /usr/src/app COPY . . EXPOSE 8004 CMD uvicorn app.main:app --reload --workers 2 --host 0.0.0.0 --port 8000
compose.yaml
services: backend: build: context: ./backend volumes: - ./backend:/usr/src/app ports: - 8004:8000 environment: - ENVIRONMENT=dev - TESTING=0 networks: mynet: ipv4_address: 10.5.0.10 depends_on: db: condition: service_healthy db: image: postgres restart: always environment: POSTGRES_PASSWORD: mypass123 ports: - "5432:5432" volumes: - db-data:/var/lib/postgresql/data networks: mynet: ipv4_address: 10.5.0.23 healthcheck: test: ["CMD", "pg_isready"] interval: 10s timeout: 5s retries: 5 admin: image: dpage/pgadmin4 restart: always environment: PGADMIN_DEFAULT_EMAIL: testemail@gmail.com PGADMIN_DEFAULT_PASSWORD: mypass123 ports: - 8080:80 volumes: - admin-data:/var/lib/pgadmin networks: mynet: volumes: db-data: admin-data: networks: mynet: driver: bridge ipam: config: - subnet: 10.5.0.0/24 gateway: 10.5.0.1
原因分析
- 本地直接运行pytest时,不会自动加载Docker Compose的环境变量,程序读取系统环境变量或默认值。
get_settings函数使用lru_cache装饰器,第一次初始化后会缓存Settings实例,后续环境变量变化不会触发重新读取。- Pydantic的
bool类型会将0解析为False,原断言中写testing: 0会导致类型不匹配。
解决方案
方案1:本地运行测试时注入环境变量
方法A:运行pytest时直接指定
ENVIRONMENT=dev TESTING=0 pytest test_main.py -v
方法B:使用.env文件加载
在backend目录下创建.env文件:
ENVIRONMENT=dev TESTING=0
修改config.py让BaseSettings读取该文件:
class Settings(BaseSettings): environment: str = "dev2" testing: bool = 1 class Config: env_file = ".env"
方案2:清除Settings缓存
修改测试文件,在测试前清除缓存,确保重新读取环境变量:
from fastapi.testclient import TestClient from app.main import app from config import get_settings client = TestClient(app) def test_read_main(): get_settings.cache_clear() # 清除缓存 response = client.get("/ping") assert response.status_code == 200 # 注意:TESTING=0会被解析为False,这里要修改断言值 assert response.json() == {"ping": "pong!", "environment": "dev", "testing": False}
方案3:在Docker容器内运行测试
临时修改backend服务命令
修改compose.yaml中backend的命令:
services: backend: # 其他配置不变 command: ["pytest", "test_main.py", "-v"]
执行测试:
docker-compose up backend
创建专门的测试服务(推荐)
在compose.yaml中添加测试服务:
services: # 原有服务不变 test: build: context: ./backend volumes: - ./backend:/usr/src/app environment: - ENVIRONMENT=dev - TESTING=0 networks: - mynet depends_on: db: condition: service_healthy command: ["pytest", "test_main.py", "-v"]
执行测试:
docker-compose run test
内容的提问来源于stack exchange,提问作者dannisis
相关产品推荐
相关产品推荐

