Pytest-django: Use fixtures on Django TestCase

Created on 4 Jan 2018  路  7Comments  路  Source: pytest-dev/pytest-django

I'm using the Django TestCase class for some of the conveniences - fixture loading, special assertions, etc - but I'd like to use some of the pytest fixtures if possible. I can use @pytest.mark.usefixtures but that doesn't to give me access to the objects returned by the fixtures, as far as I can tell. For example, if I use @pytest.mark.usefixtures("admin_client"), the admin client generated by the fixture isn't available anywhere that I can find.

Is there a way to do this that I'm missing? Or just something that's not supported?

I see in the pytest documentation that fixtures can be written to support working with unit test style TestCase classes - https://docs.pytest.org/en/latest/unittest.html#mixing-pytest-fixtures-into-unittest-testcase-subclasses-using-marks ; would that be an option for pytest-django fixtures?

Most helpful comment

@rlskoeser Something like this:

from django.test import TestCase

import pytest

from ..client import MagicClient


@pytest.mark.usefixtures("inject_attrs")
class TestGetSubjectId(TestCase):
    def test_something(self):
        result = self.admin_client.do_something()
        assert result is True


@pytest.fixture(scope="class")
def inject_attrs(request, admin_client):
    request.cls.admin_client = admin_client


@pytest.fixture
def admin_client():
    return MagicClient()

You can inject as many pytest fixtures as you want in the inject_attrs fixture. If you would rather attach the fixture to the TestCase instance, just use request.instance instead of request.cls.

Let me know if this isn't clear.

All 7 comments

@rlskoeser Yup, using the same strategy for Python unittest.TestCase as you would for django.test.TestCase works. You can either inject the pytest fixture(s) into the test class or instance, depending on what you need.

@gsong sorry, are you saying this works currently? Could you provide example code showing how I would access e.g. admin_client within a class?

@rlskoeser Have you tried something like:

import pytest


class TestOrders:
    def test_create_order(self, admin_client):
        response = admin_client.get('/myurl')
        assert response.status_code == 200

Not sure if this is what you are looking for.

@rlskoeser Something like this:

from django.test import TestCase

import pytest

from ..client import MagicClient


@pytest.mark.usefixtures("inject_attrs")
class TestGetSubjectId(TestCase):
    def test_something(self):
        result = self.admin_client.do_something()
        assert result is True


@pytest.fixture(scope="class")
def inject_attrs(request, admin_client):
    request.cls.admin_client = admin_client


@pytest.fixture
def admin_client():
    return MagicClient()

You can inject as many pytest fixtures as you want in the inject_attrs fixture. If you would rather attach the fixture to the TestCase instance, just use request.instance instead of request.cls.

Let me know if this isn't clear.

@gsong Thank you for your snippet! Unfortunately it looks like DB records of the objects created in the fixtures are not getting wiped out between calls, right? I mean if I have few test_... methods in my class, wrapped with @pytest.mark.usefixtures then the fixture get called for the each test method, but the DB is not getting cleaned.

UPD: That's because fixtures used in a class-decoration should be defined via @pytest.fixture(scope="class"), then everything is fine.

see also: #13 "Easy fixture loading"

Closing as answered.

Was this page helpful?
0 / 5 - 0 ratings