如何使用Factory_Boy创建管理员用户?

时间:2013-03-25 13:34:19

标签: python django selenium factory-boy

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

我首先遵循本教程http://www.tdd-django-tutorial.com/tutorial/1/并使用fixtures和dumpdata来为测试应用程序(创建新数据库)提供管理员帐户信息。这很好。

然后我想看看我是否可以使用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...')

无法以某种方式登录,因为它无法创建有效的管理员帐户。我怎么能用工厂男孩那样做?有可能或者我需要使用固定装置吗?

(在这篇文章中,有些人建议使用固定装置,但工厂男孩没有出现:How to create admin user in django tests.py。我也在同一个答案的底部尝试了解决方案:https://stackoverflow.com/a/3495219/1539688。它没有对我有用......)

6 个答案:

答案 0 :(得分:37)

如果你继承了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

答案 1 :(得分:7)

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

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!')

答案 2 :(得分:2)

创建管理员用户:

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

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

80

用法:

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

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

答案 3 :(得分:1)

创建用户时遇到了类似的问题。创建用户时使用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)

我使用使用Sequence生成的密码并使用Django make_password方法对其进行哈希处理。在测试中,您可以使用非散列值创建var,并使用此var创建用户。 例如:

password = 'test123'
user = UserFactory(password=my_password)

答案 4 :(得分:1)

我正在使用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)

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

答案 5 :(得分:0)

这样,我认为您可以保留代码中期望的行为,这样就可以为密码设置一个默认值,也可以在调用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