无法在 Django 单元测试中测试密码更改



我正在为Django应用程序编写一个集成测试,以测试密码是否从其他API更改。然而,在调用密码更改API之后,新密码的测试不起作用。

我在这里所做的是:我通过rest调用将密码更改为johnjohn,然后尝试检查密码是否真的更改了

我的单元测试文件

import json
import urllib
import urllib2
import requests
from django.contrib.auth.models import User
from django.test import TestCase
from adaptors.redis_class import DjangoRedis

class PasswordChange(TestCase):
    def setUp(self):
        User.objects.create_user(username='john', email='john@john.com', password='john')
        # class for redis access  
        r = DjangoRedis()
        r.set("email:john@john.com", '0' * 40, ex=3600)
        r.set('0' * 40, "email:john@john.com", ex=3600)
    def test_change_password(self):
        # Fake url I set through redis
        url = 'http://127.0.0.1:8000/reset/' + '0' * 40 + '/'
        r = requests.get(url)
        csrf_token = r.content.split('csrf_token')[1].split('"')[2]
        payload = {'csrfmiddlewaretoken': csrf_token, 'payload': json.dumps({'password': 'johnjohn'})}
        headers = {'Cookie': 'csrftoken=' + csrf_token}
        data = urllib.urlencode(payload)
        req = urllib2.Request(url, data, headers)
        response = urllib2.urlopen(req)
        json_res = json.loads(response.read())
        # This doesn't fail
        self.assertEqual(json_res['password_changed'], True)
        u = User.objects.get(username='john')
        # This fails
        self.assertEqual(u.check_password('johnjohn'),True)

我的视图

import hashlib
import random
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.http import Http404
from django.http import JsonResponse
from django.shortcuts import render
from django.views.generic import View
from adaptors.redis_class import DjangoRedis
from constants import PASSWORD_RESET
from tasks import send_email
from util.rest import get_payload
class ResetPage(View):
    def post(self, requests, reset_token):
        r = DjangoRedis()
        token = r.get(reset_token)
        if token is None:
            return JsonResponse({"token": False})
        payload = get_payload(requests.POST)
        try:
            email = token.split(':')[1].lower()
            u = User.objects.get(email=email)
            u.set_password(payload['password'])
            u.save()
            r.delete(reset_token)
            r.delete('email:' + email)
            return JsonResponse({'password_changed': True})
        except ValueError:
            return JsonResponse({"password_changed": False, 'error': "can't get your email from the database (E01)"})
        except ObjectDoesNotExist:
            return JsonResponse({"password_changed": False, 'error': "User doesn't exist (E02)"})
    def get(self, requests, reset_token):
        r = DjangoRedis()
        if not r.get(reset_token):
            raise Http404
        return render(requests, "login/reset.html", {'token': reset_token})

单元测试文件在unittests目录中称为rest_pass_login_tests.py

我使用以下命令运行它。

./manage.py test unittests/ --pattern="rest_pass_login_tests.py"

当我通过浏览器正常测试时,密码更改工作正常。然而,当我试图通过单元测试检查密码是否被更改时,我得到了一个无效的密码。

我做错了什么?

我使用开发服务器运行单元测试。当我更改密码时,我实际上是在发布到开发服务器,而不是测试服务器。

这是我的新单元测试

import json
from django.contrib.auth.models import User
from django.test import Client
from django.test import TestCase
from adaptors.redis_class import DjangoRedis

class PasswordChange(TestCase):
    def setUp(self):
        User.objects.create_user(username='john', email='john@john.com', password='john')
        r = DjangoRedis()
        r.set("email:john@john.com", '0' * 40, ex=3600)
        r.set('0' * 40, "email:john@john.com", ex=3600)
    def test_change_password(self):
        c = Client()
        payload = {'payload': json.dumps({'password': 'johnjohn'})}
        url = '/reset/' + '0' * 40 + '/'
        res = c.post(url, payload)
        res = res.json()
        self.assertEqual(res['password_changed'], True)
        u = User.objects.get(username='john')
        self.assertEqual(u.check_password('johnjohn'), True)

如果您要使用seleniumIDE,请确保您是StaticLiveServerTestCase固有的,而不是单元测试。遵循此示例。另外,不要忘记将此变量self.base_url更改为self.live_server_url

这在我的设置方法中

self.base_url = self.live_server_url

感谢fips、koterpillar在IRC频道上对我的帮助,以及aks在我下面的评论。

您没有按照视图的预期生成http post

以下是如何使用urllib2进行post:使用urllib2进行post调用而不是GET

但是,既然你已经使用了requests模块,为什么不简单地做:

url = 'http://127.0.0.1:8000/reset/' + '0' * 40 + '/'
payload = {'csrfmiddlewaretoken': csrf_token, 'payload': json.dumps({'password': 'johnjohn'})}
headers = {'Cookie': 'csrftoken=' + csrf_token}
r = requests.post(url, data=payload, headers=headers)
json_res = json.loads(r.content)

也许可以尝试调试/打印json_res的值,以检查是否有其他意外情况。

另外请注意,这是一个集成测试,因为您正在通过http进行测试,所以您的urls.py、views.py甚至settings.py都将作为其中的一部分进行测试。

您需要从数据库中刷新

self.user.refresh_from_db()

import urllib2
import requests
from django.contrib.auth.models import User
from django.test import TestCase
from adaptors.redis_class import DjangoRedis

class PasswordChange(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(username='john', email='john@john.com', password='john') #Modified
        # class for redis access  
        r = DjangoRedis()
        r.set("email:john@john.com", '0' * 40, ex=3600)
        r.set('0' * 40, "email:john@john.com", ex=3600)
    def test_change_password(self):
        # Fake url I set through redis
        url = 'http://127.0.0.1:8000/reset/' + '0' * 40 + '/'
        r = requests.get(url)
        csrf_token = r.content.split('csrf_token')[1].split('"')[2]
        payload = {'csrfmiddlewaretoken': csrf_token, 'payload': json.dumps({'password': 'johnjohn'})}
        headers = {'Cookie': 'csrftoken=' + csrf_token}
        data = urllib.urlencode(payload)
        req = urllib2.Request(url, data, headers)
        response = urllib2.urlopen(req)
        json_res = json.loads(response.read())
        # This doesn't fail
        self.assertEqual(json_res['password_changed'], True)
        self.user.refresh_from_db() #modified
        # This fails
        self.assertEqual(self.user.check_password('johnjohn'),True)```

最新更新