我一直在使用
unittest.IsolatedAsyncioTestCase
来测试我的异步方法。我一直在使用 setUpClass
和 asyncSetUp
创建固定装置,并使用 asyncTearDown
进行清理。到目前为止一切进展顺利:-)
但是现在我有一个新的要求,即为每个测试类异步创建一些固定装置,并在整个测试方法中使用它。
我知道
setUpClass
每个测试类运行一次,而 setUp
每个测试方法运行一次。 asyncSetUp
是 setUp
的异步等价物。但我似乎找不到 setUpClass
的异步等价物。
那么,每次测试异步创建和清理夹具的最佳方法是什么?
我尝试在 https://docs.python.org/3/library/unittest.html#unittest.TestCase.setUpClass 检查官方单元测试文档,但它仅记录有关
asyncSetUp
的文档。
我使用的是 Python 3.10 并使用 pytest。
没有现成的解决方案
asyncSetUpClass
,但您可以使用几种解决方案:
from unittest import IsolatedAsyncioTestCase
class TestsContext():
def __init__(self) -> None:
self.init_done: bool = False
async def set_up(self):
if self.init_done:
return
# DO INITIALIZATION HERE
self.init_done = True
class MyServiceTests(IsolatedAsyncioTestCase):
context: TestsContext = TestsContext()
async def asyncSetUp(self):
await self.context.set_up()
async def test_send_request(self):
# test implementation
import asyncio
import threading
from typing import Awaitable
from unittest import IsolatedAsyncioTestCase
def run_in_new_event_loop(worker: Awaitable, *args, **kwargs):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
thread = threading.current_thread()
print(f'Thread {thread.ident}/{thread.name} for {worker}')
return loop.run_until_complete(worker(*args, **kwargs))
finally:
if not loop.is_closed():
loop.close()
class TestsContext():
async def set_up(self):
# DO SETUP HERE
async def tear_down(self):
# DO TEAR DOWN HERE
class MyServiceTests(IsolatedAsyncioTestCase):
context: TestsContext = TestsContext()
@staticmethod
def setUpClass():
run_in_new_event_loop(MyServiceTests.context.set_up)
@staticmethod
def tearDownClass():
run_in_new_event_loop(MyServiceTests.context.tear_down)
我发现有效的方法如下:在标准
setUpClass
调用中,您可以添加一个新的事件循环作为类属性,然后运行它:
@classmethod
def setUpClass(cls) -> None:
# Handling asynchronous set up
cls.loop = asyncio.new_event_loop() # create and set new event loop
asyncio.set_event_loop(cls.loop)
cls.loop.run_until_complete(cls.asyncSetUpClass())
@classmethod
async def asyncSetUpClass(cls):
# assign measurement object to unit test object
cls.async_attribute = await AsyncFunc()
@classmethod
def tearDownClass(cls) -> None:
if cls.loop.is_running():
cls.loop.stop()
cls.loop.close()
这使我能够使用异步函数提供的属性设置多次测试所需的属性,只需一次。我不完全确定拆解的实用性,但这似乎是一个很好的措施。
我还发现我可以这样做:
@classmethod
def setUpClass(cls) -> None:
async.run(cls.asyncSetUpClass())
@classmethod
async def asyncSetUpClass(cls):
# assign measurement object to unit test object
cls.async_attribute = await AsyncFunc()