-
Notifications
You must be signed in to change notification settings - Fork 344
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 shared_db_wrapper
for creating long-lived db state
#258
base: main
Are you sure you want to change the base?
Conversation
Looks good to me so far! Would this help for keeping data from migrations with @pelme |
Sure, I'll squash it shortly. This pretty much bails out in |
04d304e
to
c2fd0cd
Compare
|
This is something I personally have been thinking about and wanting for a long time. The API and implementation looks good and simple to use! Passing request explicitly to tie the scope to the user fixture is the sensible (and possibly only way currently to handle this). This will be a killer feature for pytest-django! :) Is session/module/class fixtures that is used at the same time handled properly? I see no reason why they shouldn't be, but it would be nice with a test that uses all scopes at the same time. Transactional db support with stellar or flushes would be a really cool future addition to shared_db_wrapper. |
I can see how I could safely test mixing class and function scopes, but how would I write a test for using it with session and module scopes? |
@ktosiek It is possible to test that with a "pytester" test, that invokes a new pytest session. See for instance pytest-django/tests/test_unittest.py Lines 122 to 153 in f1711f0
The trick is to use the |
I'll push a pytester test shortly, but I also have some bad news: it looks like this won't work on Django 1.6 and 1.7, as they always close the DB connection in TransactionTestCase._post_teardown: https://github.com/django/django/blob/stable/1.7.x/django/test/testcases.py#L828 1.8 should be OK, as it only does that if some db backends don't support transactions. |
8a3eed9
to
b67b9ce
Compare
|
Hmm, couldn't we monkey patch _post_teardown in those cases and have it not close the connection? It would for sure be nice to support Django 1.6 and 1.7 if possible! |
Django 1.6 and 1.7 don't receive security updates anymore. I don't think you should refrain from merging this feature just because it doesn't work on these versions. I suggest to skip the tests on Django < 1.8. Is there anything I can do to help completing this feature? |
@aaugustin Agreed, it wouldn't be too bad to not support Django <1.7 with shared_db_wrapper, so let's avoid the complexities that would add. With your expertise in Django transaction management, if you could have a quick look at the PR that would be very helpful. Also if you could try it out in a project where you need it that would be very helpful too. There is nothing that blocks this from being merged. If I don't hear anything else I will merge it in a couple of days and push a new release. :) |
I'll try it in my current project and report back (hopefully today or tomorrow). |
I tried the patch and unfortunately I couldn't make it work. I'm getting two kinds of errors:
|
raise Exception( | ||
"shared_db_wrapper cannot be used when " | ||
"the database doesn't support transactions.") | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to me that the next 25 lines could be written as:
try:
_django_cursor_wrapper.enable()
with transaction.atomic():
yield
transaction.set_rollback(True)
finally:
_django_cursor_wrapper.restore()
This raises the question of why this fixture needs to care about _django_cursor_wrapper
; it's unclear to me why this is necessary. If it isn't, the code can be further simplified to:
with transaction.atomic():
yield
transaction.set_rollback(True)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ktosiek What do you think about that?
I'm not comfortable with pytest's internal APIs, but the general scheme here should be: with transaction.atomic():
# run shared_db_wrapper fixtures
for test in in tests:
with transaction.atomic():
# run test
transaction.set_rollback(True)
transaction.set_rollback(True) I hope this helps... |
If there's issues with this, I would be glad to help out. Let me know what's still missing. |
This still has problems: there's no way that I see to force py.test to only run It looks like this behavior depends on order of fixtures in first test function, maybe we can fix it by hooking into test collection? |
Would it help to use the decorator approach you proposed above? I think this would make sense even just for usability. I don't like how complicated it is, currently. |
Not really, the decorator would just hide the |
But with a decorator you could control the fixture order, right? You could just place it in the first spot. (Note that I don't have a lot of knowledge about py.test fixtures) |
Nope, what I need is to affect relative ordering between fixtures on the actual test. It's not about fixture's dependencies. Using a decorator won't help there. I kind of have an idea how to do that (which is a bit hacky - using pytest_runtest_setup and pre-loading some fixtures with item._request.getargvalue), but I'll have to try it (hopefully later today). |
Hmm, I see! There's probably solutions for that as well, but they are quite hacky. Wouldn't this work?:
Clearly this is hacky, but it's actually very clear what it does. Hacking around with pytest_runtest_setup is probably not cleaner (and if so, please show me :)). Also, pytest itself has very hacky origins, since it uses function names as an actual feature (which is strange, when you first start with pytest, but really cool once you get it). This leads to the fact that there's no way of modiying the function input anymore, like args/kwargs in a decorator, except for Tell me what you think! PS: This solution has a few issues when it comes to Python 2 support and certain signatures, but they are all solvable. |
as a nicer looking API? Probably. But it won't help with the fixture instantiation order (you'd need to wrap each test to move db-based fixtures after shared db fixtures) |
True. I've just realized how complicated this actually is. |
/cc @nicoddemus 😅 |
My intermediate fix BTW is to use this:
This initializes the database in the beginning and with that all fixtures can write to it. What do you guys think about this? |
Even though this is quite an old PR, I would be very much interested in the functionality it provides (without reading through the whole thread though). Why hasn't it been merged? Just because of the merge conflicts? |
IMO it's not just a simple problem and this PR doesn't solve everything, really. There are issues in the underlying pytest infrastructure/architecture that make it really hard to do this right. My personal approach has been altered a bit, I use this for a kind of large Django project:
It's not perfect. There are some major issues that you might run into. Don't ask me why I didn't make this an autouse fixture... It's just bug-prone and you have to play with it. The fixtures also might need to be defined in the right places, etc. It's just not what you really want. They need to improve some pytest things first, IMO. I also think that this issue might be a blocker and is highly relevant: pytest-dev/pytest#2405 |
IIRC this PR was blocked by problems with forcing fixture ordering (see #258 (comment)), I haven't checked if there are any new options for working around them. |
@ktosiek Looks like fixtures are now loaded in scope order, thanks to pytest-dev/pytest#3306 (which went into pytest 3.5.0). Does this help things? |
@yozlet thank you for pointing this out, it might help. But I won't have time to work on this PR this month. |
The feature of keeping db data across multiple tests would be very important in some scenarios for our tests, at the very least for performance reasons. May I help you in any way @ktosiek to get this released? |
FYI, we are having some discussion on alternatives to this PR in #514. |
This is my take on #105 and #243. This pull request adds a new fixture,
shared_db_wrapper
, that lets other fixtures access the database regardless of scope.shared_db_wrapper
is a contextmanager that creates a new savepoint/transaction, temporarily enables the cursor wrapper, and adds a finalizer through user fixture's request. This design, as opposed to making it a normal fixture, has 2 reasons:shared_db_wrapper
'sIs this approach sensible? If so, I'll be happy to write the documentation and make sure this PR is mergeable.
TODO:
db
runs after shared fixturestransaction.set_rollback(True, using=...)
instead of dummy exception