You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pytest结合Hypothesis嵌套异步函数given装饰器报错如何解决

问题场景

编写测试用例时存在两个约束:

  • 待测试的Foo类实例只能在测试函数内部生成,无法在测试文件顶层初始化
  • 使用Hypothesis生成测试值时,需要依赖Foo实例作为入参生成对应测试数据

运行pytest时固定抛出如下错误,无论是否给内部嵌套的inner函数添加@pytest.mark.asyncio装饰器都无法解决:

hypothesis.errors.InvalidArgument: Hypothesis doesn't know how to run async test functions like inner
复现代码
import pytest
from hypothesis import given
from hypothesis import strategies as st

class Foo:
    
    async def bar(value):
        return value

@st.composite
def int_datatype(draw, target):
    ...

@pytest.mark.asyncio
async def test_write_and_read_int():
    targets = [Foo(), Foo(), Foo()] # can only be generated here

    for target in targets:
        print(target)

        @pytest.mark.asyncio
        @given(int_datatype(target=target))
        async def inner(value: int):
            assert value == await target.bar(value)
        await inner()
运行环境
  • Python 3.9.4
  • pytest 7.1.2
  • pytest-asyncio 0.15.1
  • Hypothesis 6.49.1
修复方案

报错核心原因是Hypothesis在加载@given装饰器时,无法识别嵌套在其他测试函数内部的异步函数,也拿不到pytest-asyncio的异步运行上下文,靠给嵌套函数加装饰器的方式无法解决这个问题,两种可行的修复方式如下:

方案1:用pytest fixture管理Foo实例

把Foo实例的生成逻辑放到pytest fixture中,通过参数化传入测试用例,Hypothesis只负责生成独立的测试值,不需要在装饰器阶段绑定Foo实例,完全避开嵌套定义测试函数的问题:

import pytest
from hypothesis import given
from hypothesis import strategies as st

class Foo:
    async def bar(self, value):
        return value

@st.composite
def int_datatype(draw):
    # 仅负责生成int类型测试值,不依赖Foo实例
    return draw(st.integers())

# 函数级fixture,符合Foo实例只能在测试阶段生成的要求
@pytest.fixture(params=range(3))
def foo_target(request):
    return Foo()

@pytest.mark.asyncio
@given(value=int_datatype())
async def test_write_and_read_int(foo_target, value: int):
    assert value == await foo_target.bar(value)

方案2:用Hypothesis的data策略动态生成依赖实例的测试值

如果自定义的int_datatype策略必须依赖Foo实例才能生成测试数据,就使用st.data()策略,在测试函数执行、Foo实例已经初始化完成后,再动态调用draw方法生成测试值,不需要在循环内嵌套定义测试函数:

import pytest
from hypothesis import given
from hypothesis import strategies as st

class Foo:
    async def bar(self, value):
        return value

@st.composite
def int_datatype(draw, target):
    # 原有依赖target生成测试值的逻辑保持不变
    return draw(st.integers())

@pytest.mark.asyncio
@given(data=st.data())
async def test_write_and_read_int(data):
    targets = [Foo(), Foo(), Foo()]
    for target in targets:
        # 运行时再生成依赖target的测试值
        value = data.draw(int_datatype(target=target))
        assert value == await target.bar(value)

两种方案都不需要在测试函数内部动态定义新的测试函数,从根源上避开Hypothesis无法识别嵌套异步测试的问题,第二种方案完全保留了原代码中在测试函数内生成Foo实例列表的逻辑,不需要调整实例生成的位置。

内容的提问来源于stack exchange,提问作者Phil997

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 13:33:23