【问题标题】:Can't test password change in Django unittest无法在 Django unittest 中测试密码更改
【发布时间】:2016-08-29 07:01:42
【问题描述】:

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

我在这里做的是:我通过rest call将密码更改为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})

unittest 文件在unittests 目录中称为rest_pass_login_tests.py

我使用以下命令运行它。

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

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

我做错了什么?

【问题讨论】:

  • 你为什么不用 django 测试客户端而不是 urllib?
  • 我正在使用 selenium,并且我将代码更改为使用 urllib。但是,您对 django 测试客户端是正确的

标签: django python-unittest django-unittest


【解决方案1】:

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

这是我的新单元测试

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)

如果您要使用 selenium IDE,请确保您从 StaticLiveServerTestCase 继承而不是 unittest。关注this example。另外,别忘了把这个变量self.base_url改成self.live_server_url

这在我的 setUp 方法中

self.base_url = self.live_server_url

感谢 fipskoterpillar 在 IRC 频道上帮助我,感谢 aks 在我下面发表评论。

【讨论】:

    【解决方案2】:

    您没有按照您的预期创建 http post

    这里是如何使用 urllib2 发帖:Making a POST call instead of GET using urllib2

    但是,既然您已经使用了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 都将作为其中的一部分进行测试。

    【讨论】:

    • [04/May/2016 03:18:48] "GET /reset/0000000000000000000000000000000000000000/ HTTP/1.1" 200 3058 [04/May/2016 03:18:48] "POST /reset/0000000000000000000000000000000000000000/ HTTP/1.1" 200 26 不,我的帖子是正确的。不过,我会测试你的代码
    • {u'password_changed': True}。我有一个 CBV 和两种方法,一种用于获取,一种用于发布。我 100% 确定它会发帖
    • 我尝试通过 selenium 进行 E2E 测试,但遇到了同样的问题。我删除了 UI 部分并开始只做集成测试
    • 你能用ResetPage.post中缺少...的代码更新问题吗?
    • 当我通过浏览器测试代码时,它运行良好,但是在单元测试中不起作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-24
    • 2018-12-27
    • 2023-03-12
    • 2013-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多