Django 单元测试句柄 PermissionDenied

Django unittest handle PermissionDenied

正如文档所说 https://docs.djangoproject.com/en/2.2/topics/testing/tools/#exceptions;

The only exceptions that are not visible to the test client are Http404, PermissionDenied, SystemExit, and SuspiciousOperation. Django catches these exceptions internally and converts them into the appropriate HTTP response codes. In these cases, you can check response.status_code in your test.

我处理了一个自定义登录 mixin,用于向已登录但无权访问该页面的用户引发错误。

class LoginQuizMarkerMixin(LoginRequiredMixin):

    def dispatch(self, *args, **kwargs):
        if self.request.user.is_authenticated:
            if not self.request.user.has_perm('quizzes.view_sittings'):
                raise PermissionDenied("You don't have any access to this page.")
        return super().dispatch(*args, **kwargs)

我的 unitest,它工作正常。

class TestQuestionMarking(TestCase):

    def setUp(self):
        self.c1 = Category.objects.create_category(name='elderberries')
        self.student = User.objects.create_user(username='student',
                                                email='student@rebels.com',
                                                password='top_secret')
        self.teacher = User.objects.create_user(username='teacher',
                                                email='teacher@jedis.com',
                                                password='use_d@_force')
        self.teacher.user_permissions.add(Permission.objects.get(codename='view_sittings'))

        ....


    def test_paper_marking_list_view(self):
        # should be 302 (redirection)
        marking_url = reverse('quizzes:quiz_marking')
        response = self.client.get(marking_url)
        print('response1', response)

        # should be 403 (permission denied)
        self.client.login(username='student', password='top_secret')
        response = self.client.get(marking_url)
        print('response2', response)

        # should be 200 (allowed)
        self.client.login(username='teacher', password='use_d@_force')
        response = self.client.get(marking_url)
        print('response3', response)

但不知何故,它是 PermissionDenied 的错误,它显示了这样的错误:

docker-compose -f local.yml run --rm django python manage.py test labs.tests.quizzes.test_views.TestQuestionMarking.test_paper_marking_list_view


response1 <HttpResponseRedirect status_code=302, "text/html; charset=utf-8", url="/accounts/login/?next=/quizzes/marking/">


WARNING 2021-02-04 00:28:44,270 log 1 139681767683904 [log.py:222] Forbidden (Permission denied): /quizzes/marking/
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python3.8/contextlib.py", line 75, in inner
    return func(*args, **kwds)
  File "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py", line 71, in view
    return self.dispatch(request, *args, **kwargs)
  File "/app/my_app/quizzes/views.py", line 37, in dispatch
    raise PermissionDenied("You don't have any access to this page.")
django.core.exceptions.PermissionDenied: You don't have any access to this page.
response2 <HttpResponseForbidden status_code=403, "text/html; charset=utf-8">


response3 <TemplateResponse status_code=200, "text/html; charset=utf-8">

而我所期望的仍然是错误,没有像上面那样出现。应该像下面这样:

response1 <HttpResponseRedirect status_code=302, "text/html; charset=utf-8", url="/accounts/login/?next=/quizzes/marking/">

response2 <HttpResponseForbidden status_code=403, "text/html; charset=utf-8">

response3 <TemplateResponse status_code=200, "text/html; charset=utf-8">

但是,在另一个测试中,当我尝试用 raise Http404 更改它时,它工作正常:

class LoginQuizMarkerMixin(LoginRequiredMixin):

    def dispatch(self, *args, **kwargs):
        if self.request.user.is_authenticated:
            if not self.request.user.has_perm('quizzes.view_sittings'):
                # raise PermissionDenied("You don't have any access to this page.")
                raise Http404
        return super().dispatch(*args, **kwargs)

控制台输出:

response1 <HttpResponseRedirect status_code=302, "text/html; charset=utf-8", url="/accounts/login/?next=/quizzes/marking/">
WARNING 2021-02-04 13:39:05,368 log 1 139683990402880 [log.py:222] Not Found: /quizzes/marking/
response2 <HttpResponseNotFound status_code=404, "text/html; charset=utf-8">
response3 <TemplateResponse status_code=200, "text/html; charset=utf-8">

我通过在测试模式设置中禁用我的日志来解决这个问题。

docker-compose -f local.yml run --rm django python manage.py test --settings=config.settings.test

在我的 config/settings/test.py;

里面
LOGGING = {}

Related issue: How can I disable logging while running unit tests in Python Django?