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

如何在Pytest中复用不同用户Fixture生成对应认证Token?

问题描述

我在conftest.py中定义了三个session级的用户Fixture:

@pytest.fixture(scope="session")
def user1(db: Session):
    user_create = UserCreate(name="u1", email="u1@email.com", password="pass")
    return Users.create(db=db, user_create=user_create)


@pytest.fixture(scope="session")
def user2(db: Session):
    user_create = UserCreate(name="u2", email="u2@email.com", password="pass")
    return Users.create(db=db, user_create=user_create)


@pytest.fixture(scope="session")
def user3(db: Session):
    user_create = UserCreate(name="u3", email="u3@email.com", password="pass")
    return Users.create(db=db, user_create=user_create)

同时有一个工具函数user_auth_test_token,可根据用户凭证生成认证Bearer Token:

def user_auth_test_token(
    client: TestClient, db: Session, email: EmailStr, name: str, password: str
):
    user = Users.get(db=db, email=email)
    if not user:
        user = Users.create(
            db=db, user_create=UserCreate(name=name, email=email, password=password)
        )

    return _user_auth_header(client=client, email=email, password=password)

我有多个需认证的FastAPI接口,测试时需传入对应认证头,部分测试场景还需多用户同时登录。当前测试时需手动调用user_auth_test_token生成认证头,代码示例如下:

def test_auth(client: TestClient, user1: Users):
    header = user_auth_test_token(client=client, db=db, email=user1.email, name=user1.name, password='pass')
    r = client.post(f"{settings.API_STR}/users/me", headers=header)
    ...

这类样板代码重复繁琐,请问能否创建参数化的user_token Fixture,接收上述任意用户Fixture并自动生成对应认证头?有没有更优的实现方案?


解决方案

方案1:参数化user_token Fixture

通过pytest的间接参数化,实现接收任意用户Fixture并生成对应token的功能。在conftest.py中添加:

@pytest.fixture(scope="session")
def user_token(request, client: TestClient, db: Session):
    user = request.param
    return user_auth_test_token(
        client=client,
        db=db,
        email=user.email,
        name=user.name,
        password="pass"
    )

测试用例中通过parametrize指定用户Fixture:

@pytest.mark.parametrize("user_token", [pytest.lazy_fixture("user1")], indirect=True)
def test_auth(client: TestClient, user_token):
    r = client.post(f"{settings.API_STR}/users/me", headers=user_token)
    assert r.status_code == 200
    assert r.json()["email"] == "u1@email.com"

多用户场景可叠加参数化:

@pytest.mark.parametrize("user1_token", [pytest.lazy_fixture("user1")], indirect=True)
@pytest.mark.parametrize("user2_token", [pytest.lazy_fixture("user2")], indirect=True)
def test_multi_user(client: TestClient, user1_token, user2_token):
    r1 = client.post(f"{settings.API_STR}/users/me", headers=user1_token)
    assert r1.json()["name"] == "u1"
    
    r2 = client.post(f"{settings.API_STR}/users/me", headers=user2_token)
    assert r2.json()["name"] == "u2"

方案2:为每个用户单独生成token Fixture

如果常用用户数量固定,直接为每个用户创建对应token Fixture,使用更直观:

@pytest.fixture(scope="session")
def user1_token(client: TestClient, db: Session, user1: Users):
    return user_auth_test_token(
        client=client,
        db=db,
        email=user1.email,
        name=user1.name,
        password="pass"
    )

@pytest.fixture(scope="session")
def user2_token(client: TestClient, db: Session, user2: Users):
    return user_auth_test_token(
        client=client,
        db=db,
        email=user2.email,
        name=user2.name,
        password="pass"
    )

@pytest.fixture(scope="session")
def user3_token(client: TestClient, db: Session, user3: Users):
    return user_auth_test_token(
        client=client,
        db=db,
        email=user3.email,
        name=user3.name,
        password="pass"
    )

测试用例直接注入即可:

def test_auth(client: TestClient, user1_token):
    r = client.post(f"{settings.API_STR}/users/me", headers=user1_token)
    assert r.status_code == 200

def test_multi_user(client: TestClient, user1_token, user2_token):
    # 多用户逻辑实现
    pass

方案3:工厂Fixture(灵活度最高)

适合动态生成任意用户token或测试临时用户的场景,在conftest.py中添加:

@pytest.fixture(scope="session")
def get_user_token(client: TestClient, db: Session):
    def _get_token(user: Users, password: str = "pass"):
        return user_auth_test_token(
            client=client,
            db=db,
            email=user.email,
            name=user.name,
            password=password
        )
    return _get_token

测试用例中调用工厂函数生成token:

def test_auth(client: TestClient, user1: Users, get_user_token):
    user1_token = get_user_token(user1)
    r = client.post(f"{settings.API_STR}/users/me", headers=user1_token)
    assert r.status_code == 200

# 临时用户测试场景
def test_temp_user(client: TestClient, db: Session, get_user_token):
    temp_user = Users.create(db=db, user_create=UserCreate(name="temp", email="temp@test.com", password="temp_pass"))
    temp_token = get_user_token(temp_user, password="temp_pass")
    r = client.post(f"{settings.API_STR}/users/me", headers=temp_token)
    assert r.json()["name"] == "temp"

优化建议

  1. 简化user_auth_test_token:因为用户Fixture已提前创建用户,可去掉if not user的判断逻辑,减少不必要的数据库查询。
  2. 提取密码常量:如果所有测试用户密码固定为pass,可将其定义为常量,避免硬编码。
  3. 场景适配:固定用户测试选方案2,批量同逻辑测试选方案1,动态用户测试选方案3。

内容的提问来源于stack exchange,提问作者Shiladitya Bose

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 23:27:33