我想使用异步(/curio/trio)和pytest同时运行几个测试,但是我找不到关于这个的任何信息。我需要自己安排吗?如果我这样做了,有什么方法可以将(子)测试用例分离出来吗?
下面是一个我正在尝试的小玩具例子:
import pytest
import time
import asyncio
pytestmark = pytest.mark.asyncio
expected_duration = 1
accepted_error = 0.1
async def test_sleep():
start = time.time()
time.sleep(expected_duration)
duration = time.time() - start
assert abs(duration-expected_duration) < accepted_error
async def test_async_sleep():
start = time.time()
await asyncio.sleep(expected_duration)
duration = time.time() - start
assert abs(duration-expected_duration) < accepted_error发布于 2019-06-05 06:51:02
不幸的是,pytest在内部的工作方式,不能在同一个调用trio.run/asyncio.run/curio.run.的情况下同时运行多个测试(在某些方面,这也很好-它可以防止测试之间的状态泄漏,并且使用trio至少可以让您为不同的测试配置不同的trio,例如设置一个测试使用自动跳转时钟,而另一个测试不使用。)
当然,最简单的选择是使用pytest-xdist在单独的线程中运行测试。您仍然可以在每个测试内部使用异步,所有这些异步库都支持在不同的线程中运行不同的循环。
如果您确实需要使用异步并发,那么我认为您必须编写一个pytest测试函数,然后在该函数中执行您自己的调度和并发。如果您这样做,那么从pytest的角度来看,只有一个测试,因此很难获得很好的每次测试输出。我想你可以尝试使用热试验-分试验
发布于 2020-03-25 06:38:03
现在有https://github.com/willemt/pytest-asyncio-cooperative了。
现在,2020-03-25的限制相当大--您必须确保您的测试不共享anything (从技术上讲,不要共享可变状态),也不能使用mock.patch (技术上不要模拟其他测试可能使用的任何东西)。
您可以继续在https://github.com/pytest-dev/pytest-asyncio/issues/69上进行讨论,我认为这很难,但可以找到一种方法来标记每个夹具以允许或不允许并发使用,并安排测试以保留这些限制。
发布于 2019-06-05 08:37:52
正如纳撒尼尔所建议的那样,使用pytest-子测试似乎是一个可行的解决方案。下面是如何使用trio来解决这个问题,它对名称以io_开头的每个函数运行子测试。
import pytest
import sys
import trio
import inspect
import re
import time
pytestmark = pytest.mark.trio
io_test_pattern = re.compile("io_.*")
async def tests(subtests):
def find_io_tests(subtests, ignored_names):
functions = inspect.getmembers(sys.modules[__name__], inspect.isfunction)
for (f_name, function) in functions:
if f_name in ignored_names:
continue
if re.search(io_test_pattern, f_name):
yield (run, subtests, f_name, function)
async def run(subtests, test_name, test_function):
with subtests.test(msg=test_name):
await test_function()
self_name = inspect.currentframe().f_code.co_name
async with trio.open_nursery() as nursery:
for io_test in find_io_tests(subtests, {self_name}):
nursery.start_soon(*io_test)
accepted_error = 0.1
async def io_test_1():
await assert_sleep_duration_ok(1)
async def io_test_2():
await assert_sleep_duration_ok(2)
async def io_test_3():
await assert_sleep_duration_ok(3)
async def io_test_4():
await assert_sleep_duration_ok(4)
async def assert_sleep_duration_ok(duration):
start = time.time()
await trio.sleep(duration)
actual_duration = time.time() - start
assert abs(actual_duration - duration) < accepted_error运行python -m pytest -v输出:
============================ test session starts =============================
platform darwin -- Python 3.7.0, pytest-4.6.2, py-1.8.0, pluggy-0.12.0
plugins: asyncio-0.10.0, trio-0.5.2, subtests-0.2.1
collected 1 item
tests/stripe_test.py::tests PASSED [100%]
tests/stripe_test.py::tests PASSED [100%]
tests/stripe_test.py::tests PASSED [100%]
tests/stripe_test.py::tests PASSED [100%]
tests/stripe_test.py::tests PASSED [100%]
========================== 1 passed in 4.07 seconds ==========================这并不完美,因为这个百分比只是相对于测试的数量而不是子测试的数量(即。io_*在这里标记了函数),但这似乎是一个好的开始。
还请注意,time.time()是使用的,所以它对三重和异步都有意义,但是在实际的用例中,应该使用trio.current_time()。
可以使用异步实现相同的测试,基本上必须替换以下三种东西:
pytestmark = pytest.mark.trio→pytestmark = pytest.mark.asyncioyield (run, subtests, f_name, function)→yield run(subtests, f_name, function)await asyncio.gather(*find_io_tests(subtests, {self_name}))https://stackoverflow.com/questions/56448398
复制相似问题