3

I have the following code:

import asyncio
import pytest

from mymodule import myasyncfunction
from unittest import TestCase


class TestDummy(TestCase):
    def setUp(self):
        await myasyncfunction()

    @pytest.mark.asyncio
    async def test_dummy(self):
        assert False

The test passes because it doesn't enter the test at all. It only says:

RuntimeWarning: coroutine 'TestDummy.setUp' was never awaited

How to make the setUp function async?

Observation: If I remove the inheritance from TestCase the test runs but it won't enter the setUp function before, which is needed.

Damian
  • 1,084
  • 3
  • 14
  • 26

2 Answers2

3

The solution is to define a method as a fixture instead of using the traditional setUp() method.

import pytest

class TestClass:
    @pytest.fixture
    def setup(self):
        pass

    @pytest.mark.asyncio
    async def test_some_stuff(setup):
        pass

As you discovered, with pytest-asyncio the setUp() method doesn't work when a class inherits from Unitest.Testcase:

TestPersonClass is not a child class of unittest.TestCase. If it was, the test would still succeed – but the success would be a false positive because code after the await expression would not run.

Why is this happening? The answer is complex enough that it deserves a separate post, but the tl;dr version is that on line 93 of pytest-asyncio’s source the author is expecting the event loop to be passed into the test from a pytest fixture, while unittest.TestCase methods cannot directly receive fixture function arguments.

For the above explanation see end of this blog post: https://jacobbridges.github.io/post/unit-testing-with-asyncio/

For some decent tutorials on testing with pytest-asyncio see: 1)https://www.roguelynn.com/words/asyncio-testing/ 2)https://medium.com/ideas-at-igenius/testing-asyncio-python-code-with-pytest-a2f3628f82bc

Info5ek
  • 1,227
  • 4
  • 17
  • 25
-1

I encountered this issue today. In my case, it was an easy fix using this pattern:

class TestDummy:

    def setup_class(self) -> None:
        asyncio.run(do_server_registration_setup_stuff())

        # other sync setup logic...

    @pytest.mark.asyncio
    async def test_some_stuff(setup):
        pass

This may not suit your needs but for me, I am doing functional testing and so just needed to use aiohttp to run some http POST requests to register account settings etc. before the tests are run...

If you need to pre-populate some in-memory caches on the TestDummy instance you could pass self to do_server_registration_setup_stuff and set instance attributes in there.

There are probably better ways, but maybe this can help somebody to just solve the immediate problem and keep it moving!

AustEcon
  • 122
  • 1
  • 3
  • 8