pytest-django icon indicating copy to clipboard operation
pytest-django copied to clipboard

Database transactions with asyncio

Open dgilge opened this issue 7 years ago • 9 comments
trafficstars

The newly released Channels 2.0 Django package "relies on the asyncio library and native Python async support". They recommend to use py.test tests with the asyncio plugin.

Now, you can use the db and django_db fixtures with asyncio (when you use Channels' database_sync_to_async wrapper):

def create_my_model():
    return MyModel.objects.create(name='foo')

@pytest.fixture
async def add_my_model_to_db(db):
    return await database_sync_to_async(create_my_model)()

@pytest.mark.asyncio
async def test_my_db_modification(add_my_model_to_db):
    assert add_my_model_to_db.name == 'foo'
    # ...

However, transactions don't seem to work. As a result all changes to the test database will be kept.

"Note that you can’t mix this with unittest.TestCase subclasses" (which pytest-django does internally – but I don't know if this is of importance concerning this issue, because another asyncio issue seems to be related).

Is there a way to use transactions in an async setting with the current packages? Or are there plans to support transactions in async tests in the near future? Or should this be done in another package?

dgilge avatar Feb 09 '18 14:02 dgilge

Did you ever found a way around this? It seems like adding the asyncio library is affecting non-async tests as well.

vikashtank avatar Mar 09 '20 20:03 vikashtank

Possibly related: https://code.djangoproject.com/ticket/32409

Does this still happen with asgiref>=3.3.0?

bluetech avatar May 07 '21 11:05 bluetech

Yes, I'm seeing this with asgiref==3.4.1

willstott101 avatar Oct 29 '21 12:10 willstott101

There's a corresponding issue in pytest-asyncio that's been lying around for some time. I'm unsure if I can close it, so this is why I'm reaching out.

My current understanding is that pytest-django is a "pytest wrapper" around Django's unittest test cases. Is this correct?

However, pytest-asyncio does not work together with unittest as of v0.18.3 (see the README, and https://github.com/pytest-dev/pytest-asyncio/issues/77).

Is there anything that needs to be done on the pytest-asyncio side to solve this?

seifertm avatar Jun 26 '22 12:06 seifertm

I think I encountered this issue today. Marking the tests with @pytest.mark.django_db(transaction=True) instead of @pytest.mark.django_db() solved it for me.

Jenselme avatar Dec 29 '23 09:12 Jenselme

@Jenselme It works for me as well. The problem is that marking it with transaction=True slows down the tests. If you have many tests, this is not negligible.

KlemenS189 avatar Feb 02 '24 08:02 KlemenS189

Indeed, I noticed that also. I might be reverting to using the sync HTTP client and wrap async operations in async_to_sync where needed to prevent this.

I had a look at TestCase from Django which isn’t subject to this drops in performance and they wrap async tests into async_to_sync.

I’d like for a cleaner way to do this in pytest, but until then, making all test sync seem like a good alternative.

Jenselme avatar Feb 02 '24 08:02 Jenselme

Looks like a problem is because django async orm don't support atomic blocks. Pytest-django tests isolation logic bases on wrapping each test (and test fixtures) in @transaction.atomic. But async client opens new event loop inside, with self management logic.

egorgam avatar Feb 05 '24 11:02 egorgam