调用Test时覆盖method_decorator失败,Django测试补丁失效求助
补丁失效原因及解决方案
问题根源
当用@method_decorator(super_admin_login_required, name="dispatch")装饰视图类时,装饰器会在类定义阶段就完成绑定,把权限检查逻辑注入到dispatch方法中。后续用patch替换super_admin_login_required函数时,已经无法影响已经被装饰好的视图类——因为内存中的ClientActivityReport类的dispatch方法早已经是被原装饰器处理过的版本了。
可行解决方案
方案一:直接绕过视图类的装饰后的dispatch方法
直接获取视图类未被装饰的原始dispatch方法,用patch.object替换掉当前类的dispatch,跳过权限检查:
class ClientActivityTestCase(APITestCase): def test_client_activity_view_get(self): # 从类的原始属性中获取未被装饰的dispatch方法 original_dispatch = ClientActivityReport.__dict__['dispatch'] with patch.object(ClientActivityReport, 'dispatch', original_dispatch): response = self.client.get( reverse("client_activity_report"), HTTP_ORIGIN=settings.HOST_NAME, format='json' ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn("Client Activity Report", response.content.decode())
方案二:创建并登录超级管理员用户(更贴近真实业务场景)
直接构造符合权限要求的用户,让权限检查自然通过,不需要修改装饰器:
class ClientActivityTestCase(APITestCase): def test_client_activity_view_get(self): # 创建满足权限要求的超级管理员用户 super_admin = User.objects.create_superuser( username="test_superadmin", email="admin@example.com", password="testpass123", role="superadmin" # 确保User模型的role字段对应get_role_display返回"superadmin" ) # 登录该用户 self.client.login(username="test_superadmin", password="testpass123") response = self.client.get( reverse("client_activity_report"), HTTP_ORIGIN=settings.HOST_NAME, format='json' ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn("Client Activity Report", response.content.decode())
方案三:patch权限装饰器依赖的user_passes_test
super_admin_login_required本质是调用user_passes_test生成装饰器,直接patchuser_passes_test让它返回原函数,即可跳过权限检查:
from django.contrib.auth.decorators import user_passes_test class ClientActivityTestCase(APITestCase): def test_client_activity_view_get(self): # mock user_passes_test,使其不做任何权限检查直接返回原函数 def mock_user_passes_test(*args, **kwargs): def decorator(func): return func return decorator with patch('adminportal.views.user_passes_test', mock_user_passes_test): # 重新导入视图类(因为原类已被装饰,需要加载未被装饰的版本) from adminportal.views import ClientActivityReport response = self.client.get( reverse("client_activity_report"), HTTP_ORIGIN=settings.HOST_NAME, format='json' ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn("Client Activity Report", response.content.decode())
内容的提问来源于stack exchange,提问作者Yousaf Zahid
相关产品推荐
相关产品推荐

