-
Notifications
You must be signed in to change notification settings - Fork 345
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
How to use django_db mark at session fixture? #514
Comments
@ludbek we've also missed such feature and created plugin for this feature: |
@cybergrind Thanks for replying. I will definitely check it out and let you know how it went. |
This has been the biggest pain point for me coming from Django's UnitTest class-based tests, to pytest-django -- in Django we use Even if I'm just creating one DB model instance, and reloading it every TC, this is almost always faster than creating in each test case. It would be great if we could get the session-scoped fixtures from pytest-tipsi-django merged upstream, if that's a possibility; it took a bit of digging to find this issue and solution. |
hey @paultiplady I'm not sure that approach from We had to change tests in our project to this approach because we need to test some big scenarios sometimes and we've replaced existing manual transaction management in huge tests with slightly better fixtures, but it still requires attention on order tests. Right now I can see only one solution for that: putting some kind of FAQ into documentation. |
Thanks for the additional detail @cybergrind. I've dug into this a little bit more, but have run out of time for today -- here's where I've got to, I'd appreciate a sanity-check on whether this approach is useful or not, since I'm not that familiar with the pytest internals. Also I don't understand what you mean by "pytest doesn't explicitly finish unnecessary fixtures with a wider scope", could you expand on that a bit more please? Is that referring to finalizers? That might affects what I've written below. The pytest-django plugin uses the I can see a couple of options: I'm wondering if we could extend For class-scoped, and I'm assuming for session-scoped as well, the expected behaviour would be to roll back the DB afterwards, so we basically need scoped fixtures that trigger in the right order, and that each set up their own atomic transaction. I believe that means this needs to be a modification to the That way we'd correctly trigger any class-/session-level setup that is specified, and that setup would get called once per class/session. I believe a modification to the mark itself is required because if you just set up a session-scoped function that manually triggers This might look something like this (in plugin.py
Is this crazy talk, or is this thread worth exploring further? |
Regarding finalization: https://github.com/tipsi/pytest-tipsi-testing/blob/master/tests/test_finalization.py This test doesn't work without explicit finalization, same as non-function level database fixtures. And this is about pytest implementation, so there is nothing to do in pytest-django to fix it. |
Thanks, closing. |
👍 thanks! |
I would also very much appreciate this functionality. |
@mkokotovich Anyway, the main / fundamental problem here (from the original comment) is already that the DB is reset during tests, so there is no trivial way to have a session scoped fixture like that. What might work though is something along: @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 The idea being to wrap everything into an additional atomic block. This is untested however, and you might need to use |
@paultiplady |
@blueyed we're using such approach for more than a year (wrap everything into an additional atomic block) it works pretty well. |
sorry I want to clarify since I believe I'm running into the same issue: If I want to create a fixture which relies on creating a new db object, I thought I could do
However, I receive the following error on test case run: |
Just checking back in here -- still an issue, and I haven't had time to dig in further. However there has been a bit of movement upstream - there's now a https://github.com/charettes/django-testdata Any solution to this problem should probably crib the |
I took a quick look at this and actually I was able to find a partial solve to the issue above. @blueyed please take a look and let me know if you think the approach in #972 is worth progressing. (I currently haven't spent time polishing it, but am happy to do so if it looks acceptable). I did try to solve the more general problem of wiring up session fixtures, but I think it's substantially harder. I'll leave some notes in the PR explaining my findings after playing around with possible fixture APIs to solve the generic case. |
Hi @paultiplady, Replying to your PR #972 here, to keep the discussions in one place. The way pytest-django works is that it (conceptually) wraps each pytest-django test in its own django I think we should pursue the more generalized feature, and I think it can be a killer feature of pytest-django: the A previous attempt at this functionality is #258. Just yesterday I began playing with it again. First thing I needed to do is completely disable Django's Next, there's the question of the API. As others said, the most intuitive API would be @pytest.fixture(scope="module")
def items():
item1 = Item.objects.create()
item2 = Item.objects.create()
return item1 where everything created is available in the scope and is automatically rolled back at the end of the scope. I think however that it is not reasonable to start wrapping all fixtures in transactions, so I think the fixture needs to opt-in to it: @pytest.fixture(scope="module")
def items(django_test_data):
item1 = Item.objects.create()
item2 = Item.objects.create()
return item1 Turns it is not hard to implement - here it is: bluetech@fcf5ef4 I tested it with just some simple scenarios, and it works as you would expect. It still needs various usage checks like it can't be used with transactional tests etc. but that can be done I think. One problem I hit right off the bat is multi-db (for which pytest-django recently added experimental support). Currently, pytest-django implements multi-db (=> which databases to use) at the test level, i.e. every test can specify which databases it wants. But the Another thing is the new Django 3.2 Anyway, I am very interested in getting something for this in pytest-django, it's the biggest pain point I have. |
Sounds good @bluetech -- I'm happy to discard my WIP if someone with more know-how of the internals is pushing this thread forwards. A couple thoughts while this is fresh in my mind:
I had experimented with adding a class-scoped fixture that gets requested by The advantage of the approach in my PR is that it does not incur any extra DB queries over the Django
Sounds good to me. The more flexible session-scoped fixture (even if it does incur an extra query per test) would be good enough for me to remove One concern - with the approach here: @pytest.fixture(scope="module")
def items(django_test_data):
item1 = Item.objects.create()
item2 = Item.objects.create()
return item1 How does pytest handle the references to This is the problem that Basically i think you might need to find a nicer sugar for logic like: @pytest.fixture(scope="module")
def items(django_test_data):
item1 = Item.objects.create()
item2 = Item.objects.create()
return TestData(item1) Anyway, this is just based on a code read, I could easily be wrong about this. Something like this test case should catch it though if it is a problem: https://github.com/pytest-dev/pytest-django/pull/972/files#diff-82fbc96aa3f082d4667c09a2a35aec050c73120de9174e1f37bef26ef9cd3115R351-R363 |
@paultiplady your proposal would definitely be more "bulletproof" because it follows what Django does which is certain to go smoother than trying to extend it. I think the more generic approach would be more natural in pytest, and less constraining than having to work on a class level only. However, it's possible it won't pan out, in which case, we should go with your proposal, so if you'd like, I urge you to try it and see if you can make it work well.
The more generic solution would incur an extra rollback per scope it's applied to, e.g. the
In my current POC it will be shared in the scope. I believe a TestData-equivalent feature could be added with some further pytest magic, though I haven't thought of it yet. BTW, I've ran into another problem with my approach, posted a question about it to the Django internals forum: https://forum.djangoproject.com/t/why-does-django-close-db-connections-between-test-classes/10782 |
The solution given at https://pytest-django.readthedocs.io/en/latest/database.html#populate-the-database-with-initial-test-data seems to interact weirdly with multi-database support. I end up with this warning if any of my tests use multiple databases:
Though it's worth noting that my "two" databases are really just two slightly different ways of connecting to a single database. All the settings, including the name, are the same, one just has a different isolation level. Maybe this use case isn't 100% supported by the current multi-database support? It does seem to work fine as long as I don't override |
@sinjihn-4th-valley Adding
|
I'm currently solving this with |
To pile onto this: When creating, for example, some standard users and then writing a test where a user is deleted, the users are still available in the next tests as the database is rolled back after the test. Also note that every test has the standard users irrespective of the fixture being called or not. (See that no test calls
This made it easier for me to understand. Basically, every test has the full pre-populated database. |
I can confirm that using |
I have a situation where I need to load huge db fixture which is created by a function. The fixture is needed in all api tests. So I made a session fixture at
conftest.py
which would do it. But the problem ispytest
throws following exception even though I have markeddjango_db
:E Failed: Database access not allowed, use the "django_db" mark to enable it.
Below is my code snippet.
The text was updated successfully, but these errors were encountered: