Test Django with Selenium, pytest and user authentication
When testing a Django app with Selenium, how do you authenticate the user and test pages that require to be logged in?
Of course: StackOverflow has the answer.
The following is the actual code that I use to make this work with pytest. It requires pytest-django.
pytest fixtures
In pytest everything is contained in neat test fixtures.
Browser
The first fixture provides a broser/webdriver instance with an anonymous user. The default way of using Selenium.
# system_tests/conftest.py from selenium import webdriver @pytest.fixture(scope='module') def browser(request): """Provide a selenium webdriver instance.""" # SetUp options = webdriver.ChromeOptions() options.add_argument('headless') browser_ = webdriver.Chrome(chrome_options=options) yield browser_ # TearDown browser_.quit()
User
To be able to authenticate, I need a user in the database. Using Factory Boy:
# system_tests/conftest.py from django.contrib.auth.hashers import make_password from accounts.factories import UserFactory TESTEMAIL = 'test-user@example.com' TESTPASSWORD = 'a-super-secret-password' @pytest.fixture() def user(db): """Add a test user to the database.""" user_ = UserFactory.create( name='I am a test user', email=TESTEMAIL, password=make_password(TESTPASSWORD), ) return user_
Authenticated browser
To get the authenticated browser, the first two fixtures are required, plus the Django TestClient
and LiveServer
fixtures which, for pytest, are provided by pytest-django
. Using the code from SO:
# system_tests/conftest.py @pytest.fixture() def authenticated_browser(browser, client, live_server, user): """Return a browser instance with logged-in user session.""" client.login(email=TESTEMAIL, password=TESTPASSWORD) cookie = client.cookies['sessionid'] browser.get(live_server.url) browser.add_cookie({'name': 'sessionid', 'value': cookie.value, 'secure': False, 'path': '/'}) browser.refresh() return browser
The tests
Now the Selenium test can use the authenticated_browser
fixture.
# system_tests/test_django.py def test_django_with_authenticated_user(live_server, authenticated_browser): """A Selenium test.""" browser = authenticated_browser # Open the home page browser.get(live_server.url) …
To test logging in and out of the app, I use the unauthenticated browser instance plus the user
fixture.
# system_tests/test_django.py def test_login_of_anonymous_user(live_server, browser, user): # Open the home page browser.get(live_server.url) # Click the 'login' button browser.find_element_by_id('id_link_to_login')).click() …
Summary
These are the pytest fixtures that I use to test a Django app with an authenticated user.
If there is a better way to do this, please tell me! I want to know.