Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fixtures for user request factories, clients #568

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
52 changes: 52 additions & 0 deletions docs/helpers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,39 @@ Example
response = my_view(request)
assert response.status_code == 200

``django_rf_user``, ``django_rf_admin``, ``django_rf_unauth``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Additional instances of RequestFactory, which mimic the behavior of these
two middlewares:

- `django.contrib.sessions.middleware.SessionMiddleware`_
- `django.contrib.auth.middleware.AuthenticationMiddleware`_

This will add two attributes to the ``request`` object,
``request.user`` and ``request.session``.

- ``request.session`` is an in-memory `SessionBase`_ object.
- ``request.user`` depends on the fixture being used:

- ``django_rf_unauth``: an `AnonymousUser`_
- ``django_rf_user``: a normal user with no additional privileges
- ``django_rf_admin``: an admin user

::

from myapp.views import my_view

def test_details(django_rf_unauth):
request = django_rf_unauth.get('/customer/details')
response = my_view(request)
assert response.status_code == 200

.. _django.contrib.sessions.middleware.SessionMiddleware: https://docs.djangoproject.com/en/2.0/ref/middleware/#django.contrib.sessions.middleware.SessionMiddleware
.. _django.contrib.auth.middleware.AuthenticationMiddleware: https://docs.djangoproject.com/en/2.0/ref/middleware/#django.contrib.auth.middleware.AuthenticationMiddleware
.. _SessionBase: https://docs.djangoproject.com/en/2.0/topics/http/sessions/#django.contrib.sessions.backends.base.SessionBase
.. _AnonymousUser: https://docs.djangoproject.com/en/2.0/ref/contrib/auth/#django.contrib.auth.models.AnonymousUser

``client`` - ``django.test.Client``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -167,6 +200,20 @@ Example
Using the `admin_client` fixture will cause the test to automatically be marked for database use (no need to specify the
``django_db`` mark).

``django_user_client`` - ``django.test.Client`` logged in as normal user
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Same as ``admin_client``, but for a regular user.

Example
"""""""

::

def test_an_admin_view(django_user_client):
response = user_client.get('/admin/')
assert response.status_code == 403

``admin_user`` - a admin user (superuser)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand All @@ -177,6 +224,11 @@ Using the `admin_user` fixture will cause the test to automatically be marked fo
``django_db`` mark).


``django_user`` - a normal user
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Same as ``admin_user``, but for a regular user.

``django_user_model``
~~~~~~~~~~~~~~~~~~~~~

Expand Down
108 changes: 105 additions & 3 deletions pytest_django/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
from .lazy_django import skip_if_no_django

__all__ = ['django_db_setup', 'db', 'transactional_db', 'admin_user',
'django_user_model', 'django_username_field',
'client', 'admin_client', 'rf', 'settings', 'live_server',
'_live_server_helper', 'django_assert_num_queries']
'django_user_model', 'django_username_field', 'client',
'admin_client', 'django_user_client', 'rf', 'settings',
'django_user', 'django_rf_unauth', 'django_rf_user',
'django_rf_admin', 'live_server', '_live_server_helper',
'django_assert_num_queries']


@pytest.fixture(scope='session')
Expand Down Expand Up @@ -229,6 +231,36 @@ def admin_client(db, admin_user):
return client


@pytest.fixture()
def django_user(db, django_user_model, django_username_field):
"""A Django user.
This uses an existing user with username "user", or creates a new one with
password "password".
"""
UserModel = django_user_model
username_field = django_username_field

try:
user = UserModel._default_manager.get(**{username_field: 'user'})
except UserModel.DoesNotExist:
extra_fields = {}
if username_field != 'username':
extra_fields[username_field] = 'user'
user = UserModel._default_manager.create_user(
'user', 'user@example.com', 'password', **extra_fields)
return user


@pytest.fixture()
def django_user_client(db, django_user):
"""A Django test client logged in as a normal user."""
from django.test.client import Client

client = Client()
client.login(username=django_user.username, password='password')
return client


@pytest.fixture()
def rf():
"""RequestFactory instance"""
Expand All @@ -239,6 +271,76 @@ def rf():
return RequestFactory()


@pytest.fixture
def django_rf_unauth():
"""Anonymous user request factory.

This does two things to the request object:

1. Adds an anonymous user per Django's doc examples on request
factories:

https://docs.djangoproject.com/en/2.0/topics/testing/advanced/#example

This simiulates ``django.contrib.auth.middleware.AuthenticationMiddleware``.

2. Adds a naive session storage object to request.

This simiulates ``django.contrib.sessions.middleware.SessionMiddleware``,
and prevents errors on stuff like SessionWizardView in django-formtools.
"""
from django.test.client import RequestFactory
from django.contrib.auth.models import AnonymousUser
from django.contrib.sessions.backends.base import SessionBase

class UnauthRequestFactory(RequestFactory):
def request(self, **request):
r = super(UnauthRequestFactory, self).request(**request)
r.user = AnonymousUser()
r.session = SessionBase()
return r

return UnauthRequestFactory()


@pytest.fixture
def django_rf_admin(admin_user):
"""Admin user request facctory.

Mimics AuthenticationMiddleware. Also adds a memory-backed session store
to the request object."""
from django.test.client import RequestFactory
from django.contrib.sessions.backends.base import SessionBase

class AdminRequestFactory(RequestFactory):
def request(self, **request):
r = super(AdminRequestFactory, self).request(**request)
r.user = admin_user
r.session = SessionBase()
return r

return AdminRequestFactory()


@pytest.fixture
def django_rf_user(django_user):
"""Normal user request factory.

Mimics AuthenticationMiddleware. Also adds a memory-backed session store
to the request object."""
from django.test.client import RequestFactory
from django.contrib.sessions.backends.base import SessionBase

class UserRequestFactory(RequestFactory):
def request(self, **request):
r = super(UserRequestFactory, self).request(**request)
r.user = django_user
r.session = SessionBase()
return r

return UserRequestFactory()


class SettingsWrapper(object):
_to_restore = []

Expand Down
5 changes: 5 additions & 0 deletions pytest_django/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from .fixtures import django_db_createdb # noqa
from .fixtures import django_db_modify_db_settings # noqa
from .fixtures import django_db_modify_db_settings_xdist_suffix # noqa
from .fixtures import django_user # noqa
from .fixtures import _live_server_helper # noqa
from .fixtures import admin_client # noqa
from .fixtures import admin_user # noqa
Expand All @@ -31,8 +32,12 @@
from .fixtures import django_username_field # noqa
from .fixtures import live_server # noqa
from .fixtures import rf # noqa
from .fixtures import django_rf_admin # noqa
from .fixtures import django_rf_user # noqa
from .fixtures import django_rf_unauth # noqa
from .fixtures import settings # noqa
from .fixtures import transactional_db # noqa
from .fixtures import django_user_client # noqa
from .pytest_compat import getfixturevalue

from .lazy_django import django_settings_is_configured, skip_if_no_django
Expand Down
46 changes: 46 additions & 0 deletions tests/test_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import pytest

from django.db import connection, transaction
from django.contrib.sessions.backends.base import SessionBase
from django.conf import settings as real_settings
from django.core import mail
from django.test.client import Client, RequestFactory
Expand All @@ -33,25 +34,70 @@ def test_admin_client(admin_client):
assert force_text(resp.content) == 'You are an admin'


@pytest.mark.django_db
def test_user_client(django_user_client):
assert isinstance(django_user_client, Client)
resp = django_user_client.get('/admin-required/')
assert force_text(resp.content) == 'Access denied'


def test_admin_client_no_db_marker(admin_client):
assert isinstance(admin_client, Client)
resp = admin_client.get('/admin-required/')
assert force_text(resp.content) == 'You are an admin'


def test_user_client_no_db_marker(django_user_client):
assert isinstance(django_user_client, Client)
resp = django_user_client.get('/admin-required/')
assert force_text(resp.content) == 'Access denied'


@pytest.mark.django_db
def test_admin_user(admin_user, django_user_model):
assert isinstance(admin_user, django_user_model)


@pytest.mark.django_db
def test_django_user(django_user, django_user_model):
assert isinstance(django_user, django_user_model)


def test_admin_user_no_db_marker(admin_user, django_user_model):
assert isinstance(admin_user, django_user_model)


def test_django_user_no_db_marker(django_user, django_user_model):
assert isinstance(django_user, django_user_model)


def test_rf(rf):
assert isinstance(rf, RequestFactory)


def test_rf_user(django_rf_user, django_user_model):
assert isinstance(django_rf_user, RequestFactory)
request = django_rf_user.get('/admin-required/')
assert isinstance(request.user, django_user_model)
assert isinstance(request.session, SessionBase)


def test_rf_admin(django_rf_admin, django_user_model):
assert isinstance(django_rf_admin, RequestFactory)
request = django_rf_admin.get('/admin-required/')
assert isinstance(request.user, django_user_model)
assert isinstance(request.session, SessionBase)


def test_rf_unauth(django_rf_unauth, django_user_model):
assert isinstance(django_rf_unauth, RequestFactory)
request = django_rf_unauth.get('/admin-required/')
assert hasattr(request, 'user')
# AnonymousUser is a plain-object (duck-typed)
assert not isinstance(request.user, django_user_model)
assert isinstance(request.session, SessionBase)


@pytest.mark.django_db
def test_django_assert_num_queries_db(django_assert_num_queries):
with django_assert_num_queries(3):
Expand Down