如何批量测试Django视图是否应用LoginRequiredMixin或@login_required
如何批量测试Django视图的登录校验要求
当然可以!你完全可以编写一个批量测试用例,自动检查项目里除了少数例外之外的所有视图是否都应用了LoginRequiredMixin或者@login_required装饰器,这样后续新增视图时只要漏加校验,测试就会直接失败。
下面分享两种实用的实现方式:
方法一:遍历URL配置,检查视图的登录限制
这种方法会自动扫描你项目的URL路由,对每个对应的视图进行校验,你只需要指定不需要检查的视图列表即可。
完整代码示例
from django.test import TestCase from django.urls import get_resolver from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.models import User class AllViewsLoginRequiredTest(TestCase): # 这里放不需要检查登录的视图(路径是视图的完整类/函数名) EXEMPT_VIEWS = [ "django.contrib.auth.views.LoginView", "django.contrib.auth.views.LogoutView", # 加上你自己的例外视图,比如注册页、首页等 "profiles.views.PublicHomeView" ] def setUp(self): self.user = User.objects.create(username='testuser') def test_all_views_require_login(self): # 获取项目所有URL解析器 resolver = get_resolver() for url_pattern in resolver.url_patterns: # 处理包含命名空间的路由 if hasattr(url_pattern, 'url_patterns'): self._check_nested_patterns(url_pattern.url_patterns) else: self._check_view(url_pattern.callback) def _check_nested_patterns(self, patterns): # 递归处理嵌套的URL配置 for pattern in patterns: if hasattr(pattern, 'url_patterns'): self._check_nested_patterns(pattern.url_patterns) else: self._check_view(pattern.callback) def _check_view(self, callback): # 获取视图的完整标识符 view_path = f"{callback.__module__}.{callback.__name__}" if view_path in self.EXEMPT_VIEWS: return # 检查基于类的视图是否继承了LoginRequiredMixin if hasattr(callback, 'view_class'): view_class = callback.view_class self.assertIn(LoginRequiredMixin, view_class.__bases__, f"视图 {view_path} 未继承 LoginRequiredMixin") # 检查基于函数的视图是否应用了@login_required装饰器 else: # 装饰器会把原始函数存放在__wrapped__属性里 original_view = callback.__wrapped__ if hasattr(callback, '__wrapped__') else callback self.assertTrue(hasattr(original_view, '_decorated_function') and original_view._decorated_function is login_required, f"视图 {view_path} 未使用 @login_required 装饰器") def test_unauthenticated_access_redirects(self): # 额外:可以选几个关键视图测试未登录访问是否重定向(可选) test_urls = [ reverse("profiles:user_filter"), reverse("dashboard:index"), # 加上其他你想验证的URL ] for url in test_urls: response = self.client.get(url) self.assertRedirects(response, reverse("account_login") + f"?next={url}")
方法二:批量生成URL访问测试
如果你更关注“未登录访问是否会跳转到登录页”这个实际效果,可以用subTest为每个URL生成独立测试,哪个URL失败会直接明确显示:
from django.test import TestCase from django.urls import reverse, get_resolver from django.contrib.auth.models import User class LoginRequiredViewsTest(TestCase): EXEMPT_URL_NAMES = [ "account_login", "account_logout", "public_home", # 不需要检查的URL名称 ] def setUp(self): self.user = User.objects.create(username='testuser') def test_views_require_login(self): resolver = get_resolver() # 获取所有已命名的URL named_urls = [(name, reverse(name)) for name in resolver.reverse_dict.keys() if isinstance(name, str)] for name, url in named_urls: if name in self.EXEMPT_URL_NAMES: continue with self.subTest(url_name=name): response = self.client.get(url) self.assertRedirects(response, reverse("account_login") + f"?next={url}")
注意事项
- 对于基于类的视图,记得把
LoginRequiredMixin放在继承列表的最前面,避免和其他Mixin的优先级冲突。 - 如果你用了自定义的登录装饰器,只需要修改检查逻辑,替换成你的装饰器判断条件就行。
- 记得定期更新例外列表,确保不需要登录的视图被正确排除,避免误报。
这样一来,后续新增视图时只要漏加了登录校验,运行测试就会立刻报错,帮你提前把问题拦在上线前!
内容的提问来源于stack exchange,提问作者Milano
相关产品推荐
相关产品推荐

