Django测试登录失败,除非注释掉或重命名了一个完全无关的,完全琐碎的功能 [英] Django test login fails unless a completely un-related, totally trivial function is either commented out or renamed

查看:51
本文介绍了Django测试登录失败,除非注释掉或重命名了一个完全无关的,完全琐碎的功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的Django测试代码中出现一个奇怪的错误.

I'm getting a strange error in my Django testing code.

完整代码:

from .models                    import MIN_BIRTH_YEAR
from .models                    import UserProfile
from django.contrib.auth.models import User
from django.test                import TestCase
import factory

TEST_USERS = []
TEST_PASSWORD = 'password123abc'

class UserProfileFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = UserProfile
    birth_year = factory.Sequence(lambda n: n + MIN_BIRTH_YEAR - 1)

class UserFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = User
    profile = factory.RelatedFactory(UserProfileFactory, 'user')

    username = factory.Sequence(lambda n: 'test_username{}'.format(n))
    first_name = factory.Sequence(lambda n: 'test_first_name{}'.format(n))
    last_name = factory.Sequence(lambda n: 'test_last_name{}'.format(n))
    email = factory.Sequence(lambda n: 'test_email{}@example.com'.format(n))
    password = factory.PostGenerationMethodCall('set_password', TEST_PASSWORD)

def create_insert_test_users():
    for i in range(5):
        TEST_USERS.append(UserFactory.create())

def _test_one_logged_in_user(test_instance, test_user_index):
    """
    In addition to public information, private content for a single
    logged-in user should be somewhere on the page.
    """
    test_instance.client.logout()

    test_user = TEST_USERS[test_user_index]

    print('Attempting to login:')
    profile = test_user.profile
    print('test_user.id=' + str(test_user.id))
    print('   username=' + test_user.username + ', password=' + TEST_PASSWORD)
    print('   first_name=' + test_user.first_name + ', last_name=' + test_user.last_name)
    print('   email=' + test_user.email)
    print('   profile=' + str(profile))
    print('      profile.birth_year=' + str(profile.birth_year))

继续.这是我正在谈论的登录行.此 _test_one_logged_in_user 函数由以下倒数第二行( _test_one_logged_in_user(self,0))调用:

Continued. This is the login line I'm talking about. This _test_one_logged_in_user function is called by the second to last line (_test_one_logged_in_user(self, 0)) below:

    did_login_succeed = test_instance.client.login(
        username=test_user.username,
        password=TEST_PASSWORD)
    test_instance.assertTrue(did_login_succeed)

    ##########################################
    # GET PAGE AND TEST ITS CONTENTS HERE...
    ##########################################

class MainPageTestCase(TestCase):
    """Tests for the main page."""
    def setUp(self_ignored):
        """Insert test users."""
        create_insert_test_users()

    def test_true_is_true(self):
        """Public information should be somewhere on the page."""
        self.assertTrue(True)

    def test_logged_in_users(self):
        """
        In addition to public information, private content for logged in
        users should also be somewhere on the page.
        """
        _test_one_logged_in_user(self, 0)
        _test_one_logged_in_user(self, 1)

这很好.一切都过去了.但是将 test_true_is_true 的名称更改为 test_content_not_logged_in

This works fine. Everything passes. But change the name of test_true_is_true to test_content_not_logged_in

def test_content_not_logged_in(self):
    """Public information should be somewhere on the page."""
    self.assertTrue(True)

test_instance.client.login 现在返回 False ...,这导致它下面的断言

and test_instance.client.login now returns False...which results in the assertion below it

test_instance.assertTrue(did_login_succeed)

失败: AssertionError:False不正确.但是,如果您注释掉整个功能,它将成功(登录返回 True ).

to fail: AssertionError: False is not true. If you comment out the entire function, though, it succeeds (login returns True).

# def test_content_not_logged_in(self):
#     """Public information should be somewhere on the page."""
#     self.assertTrue(True)

如果取消注释并将其重命名为以下任何一项,则它将起作用:

If you uncomment it and rename it to any of the following, it works:

  • test_xcontent_not_logged_in
  • test__content_not_logged_in
  • test_not_logged_in

其中任何一个都失败了:

Any of these, and it fails:

  • test_ctrue_is_true
  • test_cxontent_not_logged_in
  • test_contentnot_logged_in
  • test_contennot_logged_in
  • test_contenot_logged_in
  • test_contnot_logged_in
  • test_connot_logged_in
  • test_cnot_logged_in
  • test_c
  • test_ctrue_is_true
  • test_cxontent_not_logged_in
  • test_contentnot_logged_in
  • test_contennot_logged_in
  • test_contenot_logged_in
  • test_contnot_logged_in
  • test_connot_logged_in
  • test_cnot_logged_in
  • test_c

(我

(I've searched for test_c and found something but nothing that indicates anything particularly special.)

这似乎暗示 setUp 函数对 test_content_not_logged_in (琐碎函数)运行一次,然后对 test_logged_in_users 运行.而且这种运行两次会导致问题.因此,我对其进行了更改,以便仅在 TEST_USER 数组为空的情况下创建用户:

This seems to imply that the setUp function runs once for test_content_not_logged_in (the trivial function), and then again for test_logged_in_users. And this running twice is causing problems. So I changed it so the users are only created if the TEST_USER array is empty:

def create_insert_test_users():
    if  len(TEST_USERS) == 0:
        for i in range(5):
            TEST_USERS.append(UserFactory.create())

但是它仍然失败,并且我可以通过ID为1的用户来确认它是否失败.

But it's still failing, and I can confirm it's failing with the user having an id of 1:

$ python -Wall manage.py test auth_lifecycle.test__view_main2
/home/jeffy/django_files/django_auth_lifecycle_venv/lib/python3.4/site.py:165: DeprecationWarning: 'U' mode is deprecated
  f = open(fullname, "rU")
/home/jeffy/django_files/django_auth_lifecycle_venv/lib/python3.4/imp.py:32: PendingDeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
  PendingDeprecationWarning)
Creating test database for alias 'default'...
.Attempting to login:
test_user.id=1
   username=test_username1, password=password123abc
   first_name=test_first_name1, last_name=test_last_name1
   email=test_email1@example.com
   profile=test_username1
      profile.birth_year=1887
F
======================================================================
FAIL: test_logged_in_users (auth_lifecycle.test__view_main2.MainPageTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/jeffy/django_files/django_auth_lifecycle/auth_lifecycle/test__view_main2.py", line 74, in test_logged_in_users
    _test_one_logged_in_user(self, 0)
  File "/home/jeffy/django_files/django_auth_lifecycle/auth_lifecycle/test__view_main2.py", line 53, in _test_one_logged_in_user
    test_instance.assertTrue(did_login_succeed)
AssertionError: False is not true

----------------------------------------------------------------------
Ran 2 tests in 0.385s

FAILED (failures=1)
Destroying test database for alias 'default'...


models.py:


models.py:

"""Defines a single extra user-profile field for the user-authentication
    lifecycle demo project:

    - Birth year, which must be between <link to MIN_BIRTH_YEAR> and
    <link to MAX_BIRTH_YEAR>, inclusive.
"""
from datetime                   import datetime
from django.contrib.auth.models import User
from django.core.exceptions     import ValidationError
from django.db                  import models

OLDEST_EVER_AGE     = 127  #:Equal to `127`
YOUNGEST_ALLOWED_IN_SYSTEM_AGE = 13   #:Equal to `13`
MAX_BIRTH_YEAR      = datetime.now().year - YOUNGEST_ALLOWED_IN_SYSTEM_AGE
"""Most recent allowed birth year for (youngest) users."""
MIN_BIRTH_YEAR      = datetime.now().year - OLDEST_EVER_AGE
"""Most distant allowed birth year for (oldest) users."""

def _validate_birth_year(birth_year_str):
    """Validator for <link to UserProfile.birth_year>, ensuring the
        selected year is between <link to OLDEST_EVER_AGE> and
        <link to MAX_BIRTH_YEAR>, inclusive.
        Raises:
            ValidationError: When the selected year is invalid.

        https://docs.djangoproject.com/en/1.7/ref/validators/

        I am a recovered Hungarian Notation junkie (I come from Java). I
        stopped using it long before I started with Python. In this
        particular function, however, because of the necessary cast, it's
        appropriate.
    """
    birth_year_int = -1
    try:
        birth_year_int = int(str(birth_year_str).strip())
    except TypeError:
        raise ValidationError(u'"{0}" is not an integer'.format(birth_year_str))

    if  not (MIN_BIRTH_YEAR <= birth_year_int <= MAX_BIRTH_YEAR):
        message = (u'{0} is an invalid birth year.'
                   u'Must be between {1} and {2}, inclusive')
        raise ValidationError(message.format(
            birth_year_str, MIN_BIRTH_YEAR, MAX_BIRTH_YEAR))
    #It's all good.

class UserProfile(models.Model):
    """Extra information about a user: Birth year.

        ---NOTES---

        Useful related SQL:
            - `select id from auth_user where username <> 'admin';`
            - `select * from auth_lifecycle_userprofile where user_id=(x,x,...);`
    """
    # This line is required. Links UserProfile to a User model instance.
    user = models.OneToOneField(User, related_name="profile")

    # The additional attributes we wish to include.
    birth_year = models.IntegerField(
        blank=True,
        verbose_name="Year you were born",
        validators=[_validate_birth_year])

    # Override the __str__() method to return out something meaningful
    def __str__(self):
        return self.user.username

推荐答案

更改测试名称时,将更改测试运行的顺序.test_logged_in_users方法在test_true_is_true之前运行,但在test_c_whatever之后运行(大概是因为它以alpha或某种顺序运行).这就是为什么您会看到名称更改的怪异之处.

When you change the name of the test, you change the order in which the tests run. The test_logged_in_users method runs BEFORE test_true_is_true but runs AFTER test_c_whatever (presumably because it's running them in alpha or some sort of order). This is why you're seeing the weirdness with the name changes.

您发现,您的setUp方法针对每个测试用例运行.首次运行setUp时,将创建用户并将其保存到数据库和TEST_USERS中.运行第二个测试时,将刷新数据库,并删除所有用户.TEST_USERS代表的用户(仍在列表中,因为您的全局变量在测试用例中仍然存在)仍然不在数据库中.

As you figured out, your setUp method runs for each test case. When your setUp runs the first time, Users are created and saved to both the DB and TEST_USERS. When your second test runs, your DB is refreshed, and all your users are deleted. The users represented by TEST_USERS (which are still in your list, because your globals persist across test cases) no longer exist in the DB.

您可以通过重置TEST_USERS来使测试通过原始代码,如下所示:

You can make your test pass in your original code by resetting TEST_USERS, like this:

def create_insert_test_users():
    # global tells python to use the TEST_USERS above, not create a new one
    global TEST_USERS
    TEST_USERS = []
    # Your code here...

现在,TEST_USERS表示与数据库中的用户匹配的新的实际用户.不过,一般而言,全局变量不是一个好主意(出于多种原因,您正在经历的困惑就在其中).即时创建它们(正如您在最新更新中所做的那样)是一种更好的解决方案.

Now, TEST_USERS represents new, real users that match users in the DB. Generally speaking, though, globals are a bad idea (for several reasons, the confusion you're experiencing being among them). Creating them on-the-fly (as you're working toward in your latest update) is a much better solution.

这篇关于Django测试登录失败,除非注释掉或重命名了一个完全无关的,完全琐碎的功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆