You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

原因分析

  1. 本地直接运行pytest时,不会自动加载Docker Compose的环境变量,程序读取系统环境变量或默认值。
  2. get_settings函数使用lru_cache装饰器,第一次初始化后会缓存Settings实例,后续环境变量变化不会触发重新读取。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 17:06:00