能否对pytest参数值进行子参数化?DRF测试参数优化问询
如何在pytest中批量分组参数化测试用例?
问题背景
开发DRF项目时,用pytest测试视图权限,遇到大量重复参数:同一个响应状态码对应多个客户端fixture,希望能按分组方式编写参数,减少冗余。
示例视图:
from rest_framework import generics from myapp.permissions import IsStaff, IsAdmin class MessagesListView(generics.ListAPIView): permission_classes = [IsStaff | IsAdmin] # ...
现有测试代码(冗余版):
import pytest from rest_framework.reverse import reverse_lazy from pytest_lazy_fixture import lazy_fixture class TestMessagesListViewPermissions: def get_response(self, client): url = reverse_lazy("api:messages-list") return client.get(url) @pytest.mark.parametrize( argnames=["client", "expected_response_status"], argvalues=[ pytest.param("anonymous_client", status.HTTP_403_FORBIDDEN), pytest.param("authenticated_client", status.HTTP_403_FORBIDDEN), pytest.param("staff_client", status.HTTP_200_OK), pytest.param("admin_client", status.HTTP_200_OK), ], ) def test__permissions(self, client, expected_response_status): response = self.get_response(client) assert response.status_code == expected_response_status
期望的参数编写方式(分组批量定义):
@pytest.mark.parametrize( argnames=["client", "expected_response_status"], argvalues=[ pytest.param(ParamList["anonymous_client", "authenticated_client"], status.HTTP_403_FORBIDDEN), pytest.param(ParamList["staff_client", "admin_client"], status.HTTP_200_OK), ], )
解决方案
方法1:手动编写参数展开函数(无需额外插件)
自己实现一个简单的展开函数,把分组参数转换成pytest能识别的格式:
def expand_param_groups(groups): expanded_args = [] for clients, status_code in groups: for client in clients: expanded_args.append(pytest.param(client, status_code)) return expanded_args
然后在测试中使用:
@pytest.mark.parametrize( argnames=["client", "expected_response_status"], argvalues=expand_param_groups([ (["anonymous_client", "authenticated_client"], status.HTTP_403_FORBIDDEN), (["staff_client", "admin_client"], status.HTTP_200_OK), ]), ) def test__permissions(self, client, expected_response_status): response = self.get_response(client) assert response.status_code == expected_response_status
这种方式完全基于pytest原生功能,灵活可控,不需要依赖第三方插件。
方法2:使用pytest-cases插件
pytest-cases是专门增强pytest参数化能力的插件,支持分组、批量展开等功能。
- 先安装插件:
pip install pytest-cases
- 使用
@expand装饰器实现批量展开:
from pytest_cases import expand class TestMessagesListViewPermissions: def get_response(self, client): url = reverse_lazy("api:messages-list") return client.get(url) @pytest.mark.parametrize( argnames=["clients", "expected_response_status"], argvalues=[ (["anonymous_client", "authenticated_client"], status.HTTP_403_FORBIDDEN), (["staff_client", "admin_client"], status.HTTP_200_OK), ], ) @expand("client", "clients") # 将clients列表展开为单个client参数 def test__permissions(self, client, expected_response_status): response = self.get_response(client) assert response.status_code == expected_response_status
- 或者用
parametrize_with_cases按case分组:
from pytest_cases import parametrize_with_cases class TestMessagesListViewPermissions: def get_response(self, client): url = reverse_lazy("api:messages-list") return client.get(url) # 定义分组case def case_unauthorized(self): return ["anonymous_client", "authenticated_client"], status.HTTP_403_FORBIDDEN def case_authorized(self): return ["staff_client", "admin_client"], status.HTTP_200_OK @parametrize_with_cases("client, expected_response_status", cases=".") def test__permissions(self, client, expected_response_status): response = self.get_response(client) assert response.status_code == expected_response_status
pytest-cases适合更复杂的参数化场景,比如跨用例共享分组、动态生成参数等。
内容的提问来源于stack exchange,提问作者Lis
相关产品推荐
相关产品推荐

