Set a shared event loop per test case

Hi all,

Is there a way to use the same event loop for all the tests in a class? We are facing an issue related to using the Strict Redis async client (we don’t use Django Redis client). Currently, we need to create a new Redis client for each test because Django creates a different event loop. This behavior is similar to IsolatedAsyncioTestCase. However, we would prefer to use a single event loop shared by all tests, eliminating the need to create multiple Redis clients.

Has anyone faced this issue?

Kind regards!

(This probably belngs in the “Using Django” category?)

Firstly, this is why your tests are running in their own event loops.

Over in django/test/testcases.py::SimpleTestCase._setup_and_call:


        # Convert async test methods.
        if iscoroutinefunction(testMethod):
            setattr(self, self._testMethodName, async_to_sync(testMethod))

async tests are just wrapped in async_to_sync (which spins up its own event loop at the point of decoration)

        # from AsyncToSync.__init__
        self.main_event_loop = None
        try:
            self.main_event_loop = asyncio.get_running_loop()
        except RuntimeError:
            # There's no event loop in this thread.
            pass

One idea: figure out how to get an event loop set up by the time test collection by the test runner is happening. I don’t know what would make sense there, and it’d be a hack.

Another idea: write yourself a decorator for your async tests. Bit silly but would at least be a touch more principled.

import asyncio
import functools

test_loop = asyncio.new_event_loop()

def share_event_loop(test):
    @functools.wraps(test)
    def wrapped(*args, **kwargs):
        coro = test(*args, **kwargs)
        return test_loop.run_until_complete(coro)
    return wrapped

Then in your test case:

class MyTestCase(TestCase):
    @share_event_loop
    async def test_some_async_thing(self):
        ...

This likely doesn’t handle much though.

At first glance, I think it could also be an acceptable change in Django to share the same event loop across a whole test process.

1 Like