测试基础 #

安装依赖 #

bash
pip install pytest
pip install httpx

TestClient #

基本使用 #

python
from fastapi import FastAPI
from fastapi.testclient import TestClient

app = FastAPI()

@app.get('/')
def read_root():
    return {'message': 'Hello World'}

client = TestClient(app)

def test_read_root():
    response = client.get('/')
    assert response.status_code == 200
    assert response.json() == {'message': 'Hello World'}

测试 POST 请求 #

python
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    price: float

@app.post('/items/')
def create_item(item: Item):
    return item

client = TestClient(app)

def test_create_item():
    response = client.post(
        '/items/',
        json={'name': 'Item 1', 'price': 10.5}
    )
    assert response.status_code == 200
    assert response.json() == {'name': 'Item 1', 'price': 10.5}

测试查询参数 #

python
@app.get('/items/')
def read_items(skip: int = 0, limit: int = 10):
    return {'skip': skip, 'limit': limit}

def test_read_items():
    response = client.get('/items/?skip=0&limit=5')
    assert response.status_code == 200
    assert response.json() == {'skip': 0, 'limit': 5}

测试路径参数 #

python
@app.get('/items/{item_id}')
def read_item(item_id: int):
    return {'item_id': item_id}

def test_read_item():
    response = client.get('/items/1')
    assert response.status_code == 200
    assert response.json() == {'item_id': 1}

测试请求头 #

python
@app.get('/headers/')
def read_headers(token: str = Header(...)):
    return {'token': token}

def test_read_headers():
    response = client.get('/headers/', headers={'token': 'secret'})
    assert response.status_code == 200
    assert response.json() == {'token': 'secret'}

pytest 使用 #

基本测试文件 #

python
from fastapi.testclient import TestClient
from main import app

client = TestClient(app)

def test_read_root():
    response = client.get('/')
    assert response.status_code == 200

def test_read_item():
    response = client.get('/items/1')
    assert response.status_code == 200
    assert response.json()['item_id'] == 1

运行测试 #

bash
pytest test_main.py

pytest -v test_main.py

pytest -v --tb=short test_main.py

使用 fixture #

python
import pytest
from fastapi.testclient import TestClient
from main import app

@pytest.fixture
def client():
    return TestClient(app)

def test_read_root(client):
    response = client.get('/')
    assert response.status_code == 200

依赖覆盖 #

覆盖依赖 #

python
from fastapi import FastAPI, Depends
from fastapi.testclient import TestClient

app = FastAPI()

async def get_db():
    return 'production_db'

@app.get('/items/')
def read_items(db: str = Depends(get_db)):
    return {'db': db}

def override_get_db():
    return 'test_db'

app.dependency_overrides[get_db] = override_get_db

client = TestClient(app)

def test_read_items():
    response = client.get('/items/')
    assert response.json() == {'db': 'test_db'}

清除覆盖 #

python
def test_read_items():
    response = client.get('/items/')
    assert response.json() == {'db': 'test_db'}
    app.dependency_overrides = {}

使用 fixture 管理覆盖 #

python
@pytest.fixture
def client():
    app.dependency_overrides[get_db] = override_get_db
    yield TestClient(app)
    app.dependency_overrides = {}

测试认证 #

测试 OAuth2 #

python
from fastapi.security import OAuth2PasswordBearer

app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl='token')

@app.get('/users/me')
def read_users_me(token: str = Depends(oauth2_scheme)):
    return {'token': token}

def test_with_token():
    response = client.get(
        '/users/me',
        headers={'Authorization': 'Bearer test-token'}
    )
    assert response.status_code == 200

测试登录 #

python
from fastapi.security import OAuth2PasswordRequestForm

@app.post('/token')
def login(form_data: OAuth2PasswordRequestForm = Depends()):
    return {'access_token': form_data.username, 'token_type': 'bearer'}

def test_login():
    response = client.post(
        '/token',
        data={'username': 'john', 'password': 'secret'}
    )
    assert response.status_code == 200
    assert 'access_token' in response.json()

测试文件上传 #

测试文件上传 #

python
from fastapi import FastAPI, File, UploadFile
from fastapi.testclient import TestClient

app = FastAPI()

@app.post('/files/')
async def create_file(file: bytes = File(...)):
    return {'file_size': len(file)}

client = TestClient(app)

def test_create_file():
    response = client.post(
        '/files/',
        files={'file': ('test.txt', b'file content')}
    )
    assert response.status_code == 200
    assert response.json() == {'file_size': 12}

测试 UploadFile #

python
@app.post('/upload/')
async def upload_file(file: UploadFile = File(...)):
    content = await file.read()
    return {'filename': file.filename, 'size': len(content)}

def test_upload_file():
    response = client.post(
        '/upload/',
        files={'file': ('test.txt', b'file content')}
    )
    assert response.status_code == 200
    assert response.json() == {'filename': 'test.txt', 'size': 12}

测试异常 #

测试 404 #

python
@app.get('/items/{item_id}')
def read_item(item_id: int):
    if item_id == 0:
        raise HTTPException(status_code=404, detail='Item not found')
    return {'item_id': item_id}

def test_item_not_found():
    response = client.get('/items/0')
    assert response.status_code == 404
    assert response.json()['detail'] == 'Item not found'

测试验证错误 #

python
@app.post('/items/')
def create_item(item: Item):
    return item

def test_validation_error():
    response = client.post('/items/', json={'name': 'Item'})
    assert response.status_code == 422

测试异步代码 #

异步测试 #

python
import pytest
import httpx
from main import app

@pytest.mark.asyncio
async def test_async():
    async with httpx.AsyncClient(app=app, base_url='http://test') as client:
        response = await client.get('/')
        assert response.status_code == 200

pytest-asyncio #

bash
pip install pytest-asyncio
python
import pytest
from httpx import AsyncClient
from main import app

@pytest.mark.asyncio
async def test_async_root():
    async with AsyncClient(app=app, base_url='http://test') as client:
        response = await client.get('/')
        assert response.status_code == 200

完整测试示例 #

python
import pytest
from fastapi import FastAPI, Depends, HTTPException
from fastapi.testclient import TestClient
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    price: float

items_db = {}

oauth2_scheme = OAuth2PasswordBearer(tokenUrl='token')

def get_current_user(token: str = Depends(oauth2_scheme)):
    if token != 'valid-token':
        raise HTTPException(status_code=401, detail='Invalid token')
    return {'username': 'testuser'}

@app.get('/')
def read_root():
    return {'message': 'Hello World'}

@app.post('/items/', response_model=Item)
def create_item(item: Item):
    items_db[item.name] = item
    return item

@app.get('/items/{item_name}', response_model=Item)
def read_item(item_name: str):
    if item_name not in items_db:
        raise HTTPException(status_code=404, detail='Item not found')
    return items_db[item_name]

@app.get('/protected/')
def protected_route(user: dict = Depends(get_current_user)):
    return {'user': user}

@pytest.fixture
def client():
    items_db.clear()
    return TestClient(app)

def test_read_root(client):
    response = client.get('/')
    assert response.status_code == 200
    assert response.json() == {'message': 'Hello World'}

def test_create_item(client):
    response = client.post('/items/', json={'name': 'Item 1', 'price': 10.5})
    assert response.status_code == 200
    assert response.json() == {'name': 'Item 1', 'price': 10.5}

def test_read_item(client):
    client.post('/items/', json={'name': 'Item 1', 'price': 10.5})
    response = client.get('/items/Item 1')
    assert response.status_code == 200
    assert response.json() == {'name': 'Item 1', 'price': 10.5}

def test_read_item_not_found(client):
    response = client.get('/items/nonexistent')
    assert response.status_code == 404

def test_protected_route_unauthorized(client):
    response = client.get('/protected/')
    assert response.status_code == 401

def test_protected_route_authorized(client):
    response = client.get(
        '/protected/',
        headers={'Authorization': 'Bearer valid-token'}
    )
    assert response.status_code == 200
    assert response.json()['user']['username'] == 'testuser'

下一步 #

现在你已经掌握了测试基础,接下来学习 Docker 部署,了解 FastAPI 的部署方法!

最后更新:2026-03-29