测试 Django 1-5 重置密码表单 - 如何为测试生成令牌?

7

以下的测试中,令牌未被识别为有效。在我的手动测试中,它是有效的,所以我想我在密码生成方面漏掉了什么。

def test_actual_reset_password(self):
    new_password = "myNewPassword012*"
    token_generator = PasswordResetTokenGenerator()
    user = UserFactory.create()
    token = token_generator.make_token(user=user)

    response = self.assert_page_loading(path="/forgot-password/reset/{0}/".format(token))
    print response 
    # That loads the page with the error message mentioning that the token was already used        

    # So I cannot carry on:
    form = response.form
    form['new_password1'] = new_password
    form['new_password2'] = new_password

    response = form.submit()

在django的源代码中,PasswordResetForm类里有这段代码;但我看不出它们之间的区别:
def save(self, ..., token_generator=default_token_generator, ...):
    """
    Generates a one-use only link for resetting password and sends to the
    user.
    """
    ...
    for user in self.users_cache:
        ...
        c = {
            ...
            'token': token_generator.make_token(user),
            ...
        }
        ...
        send_mail(subject, email, from_email, [user.email])
3个回答

16

好的,我只是在搜索如何做这个的信息,而你的问题促使我自己去弄清楚。我不确定你是否仍在处理这个问题,但以下是我让它正常工作的方法:

from django.core import mail
# First we get the initial password reset form.  
# This is not strictly necessary, but I included it for completeness
response = self.c.get(reverse('password_reset'))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.template_name, 'authentication/password_reset_form.html')

# Then we post the response with our "email address"
response = self.c.post(reverse('password_reset'),{'email':'fred@home.com'})
self.assertEqual(response.status_code, 302)
# At this point the system will "send" us an email. We can "check" it thusly:
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].subject, 'Password reset on example.com')

# Now, here's the kicker: we get the token and userid from the response
token = response.context[0]['token']
uid = response.context[0]['uid']
# Now we can use the token to get the password change form
response = self.c.get(reverse('password_reset_confirm', kwargs={'token':token,'uidb64':uid}))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.template_name, 'authentication/password_reset_confirm.html')

# Now we post to the same url with our new password:
response = self.c.post(reverse('password_reset_confirm', 
    kwargs={'token':token,'uidb36':uid}), {'new_password1':'pass','new_password2':'pass'})
self.assertEqual(response.status_code, 302)

就是这样了!其实并不难。


self.assertEqual(len(mail.outbox), 1) 中,mail 模块来自哪里? - Luis Crespo
找到了 django.core.mail - Luis Crespo
2
这在1.8版本中可能已经改变了。我在response.context[0] ? ['token']上遇到了关键错误。 - vladblindu
我将“uidb36”替换为“uidb64”,然后它就可以工作了(Django 1.9)。 - doeke
1
通过继承Django的TestCase,您可以使用self.assertTemplateUsed来缩短这些行。 - Addison Klinke
注意:为了成功应用更改,POST需要使用'token': 'set-password'。有关详细信息,请参见此线程 - Addison Klinke

0

这适用于Django 4.1:

    def test_can_reset_password(self) -> None:
        user = UserMother.random()

        uid = urlsafe_base64_encode(force_bytes(user.pk))
        token = default_token_generator.make_token(user)

        url = self._get_url(uid, token)
        redirect_response = self.client.get(url)
        redirected_url = self._get_url(uid, "set-password")
        self.assertRedirects(
            redirect_response, redirected_url, fetch_redirect_response=True
        )

        form = {"new_password1": "NEW_PASSWORD", "new_password2": "NEW_PASSWORD"}
        response = self.client.post(redirected_url, form)
        self.assertRedirects(response, self.URL_COMPLETE, fetch_redirect_response=False)

        updated_user = User.objects.get(id=user.id)
        self.assertTrue(updated_user.check_password(form["new_password1"]))

相关的导入包括:

from django.contrib.auth.tokens import default_token_generator
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode

0

这是我在功能测试中的做法:

def test_password_reset_from_key(self):
    from django.contrib.auth.tokens import default_token_generator
    from django.utils.http import base36_to_int, int_to_base36
    user = User.objects.all()[:1].get()
    token = default_token_generator.make_token(user)

    self.get("/accounts/password/reset/key/%s-%s/" % (int_to_base36(user.id), token))
    self.selenium.find_element_by_name("password1").send_keys("password")
    self.selenium.find_element_by_name("password2").send_keys("password")
    self.selenium.find_element_by_name("action").submit()

    alert = self.selenium.find_element_by_css_selector(".alert-success")
    self.assertIn('Password successfully changed.', alert.text)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接