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

Wire up cls.setUpTestData for class level fixtures #972

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

paultiplady
Copy link

Reimplement the Django TestCase's setUpTestData. Since we are
already calling the setUpClass machinery from TestCase, it's a
simple step to arrange for PytestDjangoTestCase to call the real
test class's setUpTestData classmethod.

This allows us to defer to the existing Django lifecycle hook machinery,
and also use the Django 3.2 implementation for TestData which
uses a descriptor to handle re-loading model instances between tests.
(It does this by memoizing the pre-test instances, so this avoids
us having to add a DB transaction or refresh_from_db() to reset the
testData.)

Partially fixes #514

Reimplement the Django TestCase's setUpTestData. Since we are
already calling the setUpClass machinery from TestCase, it's a
simple step to arrange for PytestDjangoTestCase to call the real
test class's setUpTestData classmethod.

This allows us to defer to the existing lifecycle hook machinery,
and also use the Django 3.2 implementation for TestData which
uses a descriptor to handle re-loading test instances between tests.
(It does this by memoizing the pre-test instances, so this avoids
us having to add a DB transaction or refresh_from_db() to reset the
testData.)
@paultiplady
Copy link
Author

WIP: So far I've just verified that unit tests run on Py 3.7, I want to get some directional feedback before spending time polishing this and getting everything green.

@paultiplady
Copy link
Author

paultiplady commented Nov 30, 2021

I looked for solutions to the more general problem of "run session-scoped Django fixtures", but it turns out to be a bit challenging. The core problem is thusly:

To write the most "pytestian" fixtures, I'd want to write a session-scoped fixture, something like

@pytest.fixture(scope="session")
def item_1():
    item_1 = Item.objects.create()
    yield item_1

However, there are a couple problems:

  1. This is session-scoped fixture, so it can't depend on the existing db function-scoped fixture. We'd need to have a new fixture to unblock the DB and do the testcase setup.

@blueyed suggested something like this:

@pytest.fixture(scope="session")
def django_db_setup(django_db_setup, django_db_blocker):
    with django_db_blocker.unblock():
        with transaction.atomic():  # XXX: could/should use `TestCase_enter_atomics` for multiple dbs
            load_time_consuming_db_fixture()
            yield

Which has the follow-on issue:

  1. this unblocks the DB for the whole session; so if you have mixed "true UTs" (no DB) and integration tests, you can't rely on pytest-django's DB locking any more. (Maybe this isn't a blocking issue? Django itself doesn't attempt to do the DB blocking, so there's an argument that it could be worth giving up the DB blocker to gain session-scoped Django fixtures.

Also, 3. This does not let us use the state-of-the-art TestData caching from Django 3.2. If we wrap everything in a transaction, even if we get this to play nicely with a TestCase (non-TransactionTestCase), we still need some way to reload the model instances to their pre-test state. (Else we're back to Django pre-3.2, where the test setUp needs to call obj.reload_from_db() on each model instance. This last objection is not insurmountable, but note that we need to do some sort of registration of model instances, and arrange for the underlying objects to get refreshed somehow -- it's not clear how we'd actually figure out in a generic way what model instances were created.

Maybe I'm overcomplicating this though -- I'm not familiar with the pytest machinery, so it's very likely there's an approach I didn't think of.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

How to use django_db mark at session fixture?
1 participant