如何在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"
优化建议
- 简化
user_auth_test_token:因为用户Fixture已提前创建用户,可去掉if not user的判断逻辑,减少不必要的数据库查询。 - 提取密码常量:如果所有测试用户密码固定为
pass,可将其定义为常量,避免硬编码。 - 场景适配:固定用户测试选方案2,批量同逻辑测试选方案1,动态用户测试选方案3。
内容的提问来源于stack exchange,提问作者Shiladitya Bose
相关产品推荐
相关产品推荐

