Как создать пользователя-администратора с помощью Factory_Boy?
Я относительный начинающий Django и просто начал выполнять некоторые тесты для своих проектов. Я хочу создать функциональный тест с селеном, который входит в сайт администратора Django.
Сначала я познакомился с этим учебным пособием http://www.tdd-django-tutorial.com/tutorial/1/ и использовал инструменты и дампдаты, чтобы сделать информацию учетной записи администратора доступной для тестового приложения (которое создает новую базу данных). Это прекрасно работает.
Затем я хотел посмотреть, смогу ли я сделать то же самое с заводским мальчиком, чтобы заменить светильники. Фабричный мальчик работает, создавая необходимый объект в файле tests.py, который кажется мне более чистым. Почему-то я не могу заставить это работать, и документация Factory_boy не слишком полезна...
Вот мой test.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...')
Это не позволяет войти в систему, поскольку каким-то образом он не создает действительную учетную запись администратора. Как я могу это сделать с помощью заводского мальчика? Возможно ли, или мне нужно использовать светильники для этого?
(В этом посте некоторые люди предложили светильники, но заводский мальчик не появился: как создать пользователя admin в django tests.py. Я также попытался предложить решение, предложенное внизу в том же ответе: qaru.site/info/232745/.... Это не сработало для меня...)
Ответы
Ответ 1
Если вы подклассифицируете factory.DjangoModelFactory, он должен сохранить объект пользователя для вас. См. Раздел примечания в разделе PostGenerationMethodCall. Тогда вам нужно только сделать следующее:
class UserFactory(factory.DjangoModelFactory):
FACTORY_FOR = User
email = '[email protected]'
username = 'admin'
password = factory.PostGenerationMethodCall('set_password', 'adm1n')
is_superuser = True
is_staff = True
is_active = True
Ответ 2
Я предполагаю, что вы работаете над учебником 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 = '[email protected]'
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!')
Ответ 3
Создание пользователей-администраторов:
Вы можете добавить объявление Params, которое обеспечивает гибкость для быстрого создания пользователя-администратора или обычного пользователя.
https://factoryboy.readthedocs.io/en/latest/introduction.html?highlight=class%20Params#altering-a-factory-s-behaviour-parameters-and-traits
Установка необработанных паролей:
Чтобы гарантировать, что параметр пароля будет установлен как необработанное незашифрованное значение, вы можете использовать Django set_password, чтобы установить необработанный пароль после первоначального сохранения в переопределении метода _create classmethod.
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 '%[email protected]' % 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
Ответ 4
У меня была аналогичная проблема при создании пользователя. 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 и hash, используя метод Django make_password
. В тестах вы можете создать var с не хешированным значением и создать пользователя с этим var. Пример:
password = 'test123'
user = UserFactory(password=my_password)
Ответ 5
Вот как я это сделал с Factory Boy 2.7.0
и Django 1.8.4
. Ниже представлены две фабрики: одна для базового пользователя и одна для администратора, которая наследует от базового добавления только необходимые поля.
class UserFactory(DjangoModelFactory):
class Meta:
model = User
django_get_or_create = ('username', 'email', 'password')
username = 'basic'
email = '[email protected]_admin.com'
password = 'basicpassword'
class AdminFactory(UserFactory):
username = 'admin'
is_superuser = True
Ответ 6
Я использую 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
[email protected]
Используйте адрес электронной почты, полученный от user.username
и пароль 'pi3.1415'
чтобы войти в систему с user.username
администратора.
Что, если у пользователя есть обратные внешние ключи?
Простой, скажем, у вас есть 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 = make_password('pi3.1415')
is_staff = True
is_superuser = True
profile = factory.RelatedFactory(ProfileFactory, 'user', visited=True)
Для этого используйте ту же логику в примере, чтобы создать своего суперпользователя.