Django中的单元测试没有看到成功登录的正确重定向网址?

时间:2017-09-13 12:47:06

标签: python django unit-testing django-testing django-1.11

在我的Django项目中,我创建了自定义管理页面(不是来自Django的管理员)。我有2个登录页面。一个用于管理员,另一个用于其他用户。

下面您可以看到管理员的 urls.py 文件。我测试它,它工作正常。成功登录后,Django会将用户重定向到 next 参数(/administration/dashboard/)的网址。

我写了单元测试,它引发了错误。从错误我明白Django重定向到默认网址(/accounts/profile/)。为什么单元测试不使用我在urls.py文件中做的设置(下一个参数)?

如何解决这个问题?

现在我注意到只有在 settings.py 中使用此代码LOGIN_REDIRECT_URL = '/administration/dashboard/'时问题才会消失。我无法使用它,因为将来我会将LOGIN_REDIRECT_URL用于我的其他登录页面。

我将不胜感激任何帮助!

urls.py:

from django.contrib.auth import views as authentication_views

urlpatterns = [
    # Administration Login
    url(r'^login/$',
        authentication_views.login,
        {
            'template_name': 'administration/login.html',
            'authentication_form': AdministrationAuthenticationForm,
            'extra_context': {
                'next': reverse_lazy('administration:dashboard'),
            },
            'redirect_authenticated_user': True
        },
        name='administration_login'),
]

tests.py:

class AdministrationViewTestCase(TestCase):
    def setUp(self):
        self.client = Client()
        self.credentials = {'username': 'user', 'password': 'password'}
        self.user = User.objects.create_user(self.credentials, is_staff=True)
        self.data = dict(
            self.credentials,
            next=reverse("administration:dashboard")
        )

    def test_administration_authorization(self):
        self.assertTrue(self.user)

        # logged_in = self.client.login(**self.credentials)
        # self.assertTrue(logged_in)

        response = self.client.post(
            reverse("administration:administration_login"),
            self.data,
            follow=True
        )
        # self.assertEqual(response.status_code, 302)
        self.assertRedirects(
            response,
            reverse("administration:dashboard"),
            status_code=302,
            target_status_code=200
        )

错误:

Traceback (most recent call last):
  File "/home/nurzhan/CA/administration/tests.py", line 51, in test_administration_authorization
    reverse("administration:dashboard"),
  File "/srv/envs/py27/lib/python2.7/site-packages/django/test/testcases.py", line 271, in assertRedirects
    % (response.status_code, status_code)
AssertionError: Response didn't redirect as expected: Response code was 200 (expected 302)

forms.py:

from django import forms
from django.contrib.auth.forms import AuthenticationForm
from django.utils.translation import ugettext_lazy as _


class AdministrationAuthenticationForm(AuthenticationForm):
    """
        A custom authentication form used in the administration application.
    """
    error_messages = {
        'invalid_login': (
            _("ERROR MESSAGE.")
        ),
    }
    required_css_class = 'required'

def confirm_login_allowed(self, user):
    if not user.is_active or not user.is_staff:
        raise forms.ValidationError(
            self.error_messages['invalid_login'],
            code='invalid_login',
            params={
                'username': self.username_field.verbose_name
            }
        )

的login.html:

<form action="{% url 'administration:administration_login' %}" method="post">
  {% csrf_token %}
  {{ form.as_p }}
  <input type="submit" value="Submit">
  <input type="hidden" name="next" value="{{ next }}"/>
</form>

2 个答案:

答案 0 :(得分:0)

Django isn't checking next in extra_context但在GETPOST参数中。

extra_context is used to update your template context。因此,如果您想将变量值传递给模板,可以使用extra_context设置它们。

但是,您可以通过setting LOGIN_REDIRECT_URL in settings.py修复测试,或者将其作为POST或GET参数传递给下一个。

params = dict(**self.credentials, next=reverse("home:index"))
response = self.client.post(
    url,
    params,
    follow=True
)

注意:

  

self.credentials应该在应用于self.data的字典中解压缩。

self.data = dict(
    **self.credentials,
    next=reverse("administration:dashboard")
)

如果您想在不进行此更改的情况下进行测试,我可以推荐另一种方法来测试它。

您需要在浏览器中呈现模板并填写登录表单。这样,您的浏览器客户端就会将POST请求作为参数传递next。 您可以使用selenium webdriver无头浏览器完成此操作。

答案 1 :(得分:0)

您是否尝试删除POST请求中的follow=True。您正在检查重定向,但是您告诉requests模块遵循重定向,因此您的响应将直接是页面,而不是302重定向HTTP响应。

更明确。您正在发送requests.post(follow=True)的请求,该请求将遵循302重定向到目标网页,而您的response将是带有目标网页的HTTP 200。即使目标页面是您想要的页面,测试断言也会失败,因为您在响应中查找了HTTP 302代码并且您已经遵循了重定向。