30

我是一个相对的 Django 初学者,刚刚开始为我的项目做一些测试。我想要做的是使用登录到 Django Admin 站点的 selenium 构建一个功能测试。

我首先遵循本教程http://www.tdd-django-tutorial.com/tutorial/1/并使用固定装置和转储数据使管理员帐户信息可用于测试应用程序(它创建一个新数据库)。这工作正常。

然后我想看看我是否可以使用factory-boy来更换固定装置。工厂男孩通过在 tests.py 文件中实例化必要的对象来工作,这对我来说似乎更干净。不知何故,我无法让它工作,而且 Factory_boy 文档也没有太大帮助......

这是我的tests.py

from django.test import LiveServerTestCase
from django.contrib.auth.models import User
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import factory

class UserFactory(factory.Factory):
    FACTORY_FOR = User

    username = 'jeff'
    password = 'pass'
    is_superuser = True

class AdminTest(LiveServerTestCase):

    def setUp(self):
        self.browser = webdriver.Firefox()

    def tearDown(self):
        self.browser.quit()

    def test_if_admin_login_is_possible(self):
        jeff = UserFactory.create()

        # Jeff opens the browser and goes to the admin page
        self.browser = webdriver.Firefox()
        self.browser.get(self.live_server_url + '/admin/')

        # Jeff sees the familiar 'Django Administration' heading
        body = self.browser.find_element_by_tag_name('body')
        self.assertIn('Django administration', body.text)

        # Jeff types in his username and password and hits return
        username_field = self.browser.find_element_by_name('username')
        username_field.send_keys(jeff.username)
        password_field = self.browser.find_element_by_name('password')
        password_field.send_keys(jeff.password)
        password_field.send_keys(Keys.RETURN)

        # Jeff finds himself on the 'Site Administration' page
        body = self.browser.find_element_by_tag_name('body')
        self.assertIn('Site administration', body.text)

        self.fail('Fail...')

这无法登录,因为它没有创建有效的管理员帐户。我怎么能用 factory-boy 做到这一点?有可能还是我需要为此使用固定装置?

(在这篇文章中,有些人建议固定装置是必要的,但工厂男孩没有出现:如何在 django tests.py 中创建管理员用户。我还尝试了相同答案底部建议的解决方案:https://stackoverflow。 com/a/3495219/1539688。它对我不起作用......)

4

6 回答 6

50

如果你继承 factory.DjangoModelFactory 它应该为你保存用户对象。请参阅PostGenerationMethodCall下的注释部分。那么您只需要执行以下操作:

class UserFactory(factory.DjangoModelFactory):
    FACTORY_FOR = User

    email = 'admin@admin.com'
    username = 'admin'
    password = factory.PostGenerationMethodCall('set_password', 'adm1n')

    is_superuser = True
    is_staff = True
    is_active = True
于 2013-07-31T20:32:50.730 回答
11

我正在使用 Django 1.11(我敢打赌它将在 Django 2+ 中工作)和 factory_boy 2.11.1。这很简单:

import factory
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import User


class SuperUserFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = User

    first_name = factory.Faker('first_name')
    last_name = factory.Faker('last_name')
    username = factory.Faker('email')
    password = factory.LazyFunction(lambda: make_password('pi3.1415'))
    is_staff = True
    is_superuser = True

在这个例子中,如果你想要一些不同的东西,所有用户都将'pi3.1415'相应地更改密码,或者你甚至可以使用它password = factory.Faker('password')来生成一个随机密码(但是,它应该是你能够弄清楚的。否则,将很难登录)。

创建超级用户的示例

>>> user = SuperUserFactory.create()
>>> user.username # the following output will be different in your case
amber60@hotmail.com

使用您收到的电子邮件user.username和密码'pi3.1415'登录管理员。

如果用户关联了反向外键怎么办?

很简单,假设您有一个模型,该模型Profile具有模型的外键User。然后你必须添加以下类:

class Profile(models.Model):
    user = models.OneToOneField(User)
    visited = models.BooleanField(default=False)


# You need to set the foreign key dependency using factory.SubFactory
class ProfileFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Profile

    user = factory.SubFactory(UserFactory)


# use a RelatedFactory to refer to a reverse ForeignKey 
class SuperUserFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = User

    first_name = factory.Faker('first_name')
    last_name = factory.Faker('last_name')
    username = factory.Faker('email')
    password = factory.LazyFunction(lambda: make_password('pi3.1415'))
    is_staff = True
    is_superuser = True
    profile = factory.RelatedFactory(ProfileFactory, 'user', visited=True)

就是这样,使用示例中的相同逻辑来创建您的超级用户。

于 2019-04-05T04:44:06.620 回答
7

我假设您正在研究http://www.tdd-django-tutorial.com教程,因为这也是我被卡住的地方。您现在可能已经想通了,但是对于下一个人来说,这是对我有用的代码,诀窍是添加 _prepare 方法以确保密码被加密,并将所有标志设置为 true(这是使用 Django 1.5 完成的。 1、如果您使用的是早期版本,请更改 User 模型导入)

from django.test import LiveServerTestCase
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

import factory
from django.contrib.auth import get_user_model
User = get_user_model()


class UserFactory(factory.DjangoModelFactory):
    FACTORY_FOR = User

    email = 'admin@admin.com'
    username = 'admin'
    password = 'adm1n'

    is_superuser = True
    is_staff = True
    is_active = True

    @classmethod
    def _prepare(cls, create, **kwargs):
        password = kwargs.pop('password', None)
        user = super(UserFactory, cls)._prepare(create, **kwargs)
        if password:
            user.set_password(password)
            if create:
                user.save()
        return user

class PollsTest(LiveServerTestCase):

    def setUp(self):
        self.browser = webdriver.Firefox()
        self.browser.implicitly_wait(3)
        self.user = UserFactory.create()


    def tearDown(self):
        self.browser.quit()

    def test_can_create_new_poll_via_admin_site(self):
        self.browser.get(self.live_server_url+'/admin/')

        body = self.browser.find_element_by_tag_name('body')
        self.assertIn('Django administration', body.text)

        username_field = self.browser.find_element_by_name('username')
        username_field.send_keys(self.user.username)

        password_field = self.browser.find_element_by_name('password')
        password_field.send_keys('adm1n')
        password_field.send_keys(Keys.ENTER)

        body = self.browser.find_element_by_tag_name('body')
        self.assertIn('Site administration', body.text)

        polls_links = self.browser.find_element_by_link_text('Polls')
        self.assertEqual(len(polls_links), 2)


        self.fail('Finish the test!')
于 2013-05-31T16:56:07.600 回答
7

创建管理员用户:

您可以添加一个参数声明,该声明提供了快速创建管理员用户或普通用户的灵活性。

https://factoryboy.readthedocs.io/en/latest/introduction.html?highlight=class%20Params#altering-a-factory-s-behaviour-parameters-and-traits

设置原始密码:

为了确保将密码参数设置为原始未加密值,您可以在 _create 类方法覆盖的初始保存后使用 Django 的 set_password 设置原始密码。

https://docs.djangoproject.com/en/2.1/ref/contrib/auth/#django.contrib.auth.models.User.set_password

class UserFactory(factory.django.DjangoModelFactory):
    first_name = factory.Faker('first_name')
    last_name = factory.Faker('last_name')
    username = factory.Sequence(lambda n: 'demo-user-%d' % n)
    is_staff = False
    is_superuser = False
    password = 'secret'

    @factory.lazy_attribute
    def email(self):
        return '%s@test.com' % self.username

    class Meta:
        model = User

    class Params: 
        # declare a trait that adds relevant parameters for admin users
        flag_is_superuser = factory.Trait(
            is_superuser=True,
            is_staff=True,
            username = factory.Sequence(lambda n: 'admin-%d' % n),
        )

    @classmethod
    def _create(cls, model_class, *args, **kwargs):
        password = kwargs.pop("password", None)
        obj = super(UserFactory, cls)._create(model_class, *args, **kwargs)
        # ensure the raw password gets set after the initial save
        obj.set_password(password)
        obj.save()
        return obj

用法:

# This admin user can log in as "admin-1", password "secret"
admin = UserFactory.create(flag_is_superuser=True)

# This regular user can log in as "userABC", password "secretABC"
user = UserFactory.create(username="userABC", password="secretABC")

使用 factory-boy v2.11.1 和 Django v1.11.6

于 2019-02-07T23:45:58.253 回答
2

创建用户时我遇到了类似的问题。创建用户时的 Django 哈希密码,并且您使用没有哈希的 DjangoFactory 保存密码。登录时,Django 会检查您使用存储的哈希值发送的密码。在这一步验证失败,因为您使用未哈希密码检查未哈希密码。这是我如何在代码中解决此问题的示例:

from django.contrib.auth.hashers import make_password
from factory import DjangoModelFactory, Sequence



class UserFactory(DjangoModelFactory):
    class Meta:
        model = User
        django_get_or_create = ('username', 'password')

    username = Sequence(lambda n: 'somename%s' % n)
    password = Sequence(lambda p: 'mysuperpass%s' % p)

    @classmethod
    def _create(cls, model_class, *args, **kwargs):
        """Override the default ``_create`` with our custom call."""
        kwargs['password'] = make_password(kwargs['password'])
        return super(UserFactory, cls)._create(model_class, *args, **kwargs)

我获取使用序列生成的密码并使用 Djangomake_password方法对其进行哈希处理。在测试中,您可以创建一个没有散列值的 var,并使用这个 var 创建一个用户。例子:

password = 'test123'
user = UserFactory(password=my_password)
于 2018-01-17T10:35:05.187 回答
1

这样我认为您可以保留您在代码中期望的行为,这样您就有了密码的默认值,并且您可以在调用 UserFactory 时使用您想要的任何值覆盖它。

class UserFactory(factory.Factory):
    FACTORY_FOR = User

    username = 'jeff'
    password = 'pass'
    is_superuser = True
    
    @classmethod
    def _create(cls, model_class, *args, **kwargs):
        """Create an instance of the model, and save it to the database."""
        if cls._meta.django_get_or_create:
            return cls._get_or_create(model_class, *args, **kwargs)

        manager = cls._get_manager(model_class)
        return manager.create_user(*args, **kwargs) # Just user the create_user method recommended by Django
于 2020-09-11T03:48:15.903 回答