Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How Can You Create an Admin User with Factory_Boy?

I'm a relative Django beginner and just started doing some testing for my projects. What I want to do is build a functional test with selenium that logs into the Django Admin site.

I first followed this tutorial http://www.tdd-django-tutorial.com/tutorial/1/ and used fixtures and dumpdata to make the admin account info available for the testing app (which creates a new database). This works fine.

I then wanted to see if I can do the same using factory-boy to replace the fixtures. Factory boy works by instantiating the necessary object within the tests.py file which seems cleaner to me. Somehow I can't get this to work and the Factory_boy documentation is not too helpful...

Here is my 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...') 

This fails to log in as somehow it doesn't create a valid admin account. How can I do that using factory-boy? Is it possible or do I need to use fixtures for that?

(In this post some people suggested fixtures are necessary but factory boy didn't come up: How to create admin user in django tests.py. I also tried the solution suggested at the bottom in the same answer: https://stackoverflow.com/a/3495219/1539688. It didn't work for me...)

like image 295
Brian Fabian Crain Avatar asked Mar 25 '13 13:03

Brian Fabian Crain


2 Answers

If you subclass factory.DjangoModelFactory it should save the user object for you. See the note section under PostGenerationMethodCall. Then you only need to do the following:

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 
like image 132
emispowder Avatar answered Sep 20 '22 12:09

emispowder


I am using Django 1.11 (I can bet it will work in Django 2+) and factory_boy 2.11.1. This was pretty simple:

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 

In this example, all users will have password 'pi3.1415' change it accordingly if you want something different, or you can even use password = factory.Faker('password') to generate a random password (however, it should be something you are able to figure out. Otherwise, it will be very hard to log in).

Example creating a superuser

>>> user = SuperUserFactory.create() >>> user.username # the following output will be different in your case [email protected] 

Use the email you got from user.username and the password 'pi3.1415' to log in in the admin.

What if the user has reverse foreign keys associated?

Simple, let's say you have a model Profile which has a foreign key to your User model. Then you have to add the following classes:

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) 

That's it, use the same logic in the example to create your superuser.

like image 43
lmiguelvargasf Avatar answered Sep 24 '22 12:09

lmiguelvargasf