Skip to content

pytest 实践 ​

实验目标 ​

编写完整的测试套件,覆盖 API 的各个功能点。

实验 1:基础测试结构 ​

安装依赖:

bash
pip install pytest pytest-cov httpx

创建 test_main.py:

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
    assert response.json() == {"message": "Hello World"}

运行测试:

bash
pytest
pytest -v  # 详细输出
pytest -s  # 显示 print 输出

实验 2:参数化测试 ​

python
import pytest

@pytest.mark.parametrize("user_id,expected_status", [
    (1, 200),
    (999, 404),
])
def test_get_user(user_id, expected_status):
    response = client.get(f"/users/{user_id}")
    assert response.status_code == expected_status

@pytest.mark.parametrize("email,username,password,expected_status", [
    ("valid@example.com", "validuser", "password123", 201),
    ("invalid-email", "user", "password", 422),
    ("", "user", "password", 422),
])
def test_create_user_validation(email, username, password, expected_status):
    response = client.post("/users/", json={
        "email": email,
        "username": username,
        "password": password
    })
    assert response.status_code == expected_status

实验 3:Fixture 使用 ​

python
import pytest

@pytest.fixture
def created_user():
    response = client.post("/users/", json={
        "email": "fixture@example.com",
        "username": "fixtureuser",
        "password": "password123"
    })
    return response.json()

def test_get_created_user(created_user):
    user_id = created_user["id"]
    response = client.get(f"/users/{user_id}")
    assert response.status_code == 200
    assert response.json()["email"] == "fixture@example.com"

def test_delete_created_user(created_user):
    user_id = created_user["id"]
    response = client.delete(f"/users/{user_id}")
    assert response.status_code == 204

实验 4:异步测试 ​

python
import pytest
from httpx import AsyncClient
from main import app

@pytest.mark.asyncio
async def test_async_endpoint():
    async with AsyncClient(app=app, base_url="http://test") as ac:
        response = await ac.get("/users/")
    assert response.status_code == 200

实验 5:Mock 外部依赖 ​

python
from unittest.mock import patch, MagicMock

def test_external_api_call():
    with patch('main.external_api_call') as mock_api:
        mock_api.return_value = {"data": "mocked"}
        
        response = client.get("/external-data")
        assert response.status_code == 200
        assert response.json() == {"data": "mocked"}
        mock_api.assert_called_once()

实验 6:数据库测试 ​

python
@pytest.fixture(scope="function")
def test_db():
    # 创建测试数据库
    Base.metadata.create_all(bind=engine)
    yield
    # 清理测试数据库
    Base.metadata.drop_all(bind=engine)

def test_user_crud(test_db):
    # Create
    response = client.post("/users/", json={
        "email": "crud@example.com",
        "username": "cruduser",
        "password": "password123"
    })
    assert response.status_code == 201
    user_id = response.json()["id"]
    
    # Read
    response = client.get(f"/users/{user_id}")
    assert response.status_code == 200
    
    # Update
    response = client.put(f"/users/{user_id}", json={
        "email": "updated@example.com",
        "username": "updateduser"
    })
    assert response.status_code == 200
    
    # Delete
    response = client.delete(f"/users/{user_id}")
    assert response.status_code == 204

验收标准 ​

✅ 所有测试通过
✅ 测试覆盖率 > 80%
✅ 正常和异常情况都有测试
✅ 使用 fixture 复用测试数据
✅ 参数化测试减少重复代码

返回第四章概览

用心记录每一步成长