如何在Python Hypothesis测试中结合@pytest.fixture与@given装饰器?
如何结合pytest fixture与Hypothesis的given参数化?
我正在使用pytest和Hypothesis做Python的基于属性测试。我想测试自定义Matrix类的构造函数是否正确分配行列数,希望两个独立测试函数共享同一个实例(用pytest.fixture实现),同时用hypothesis.given装饰器,通过hypothesis.strategies.integers生成的随机seed参数化测试。
尝试直接给fixture加@given装饰器时,出现fixture 'matrix' not found错误,请问能不能结合这两个特性?如果不能,有什么替代方案?
原可运行测试示例:
import numpy as np from pytest import fixture class Matrix: def __init__(self, nrows, ncols, seed=None): np.random.seed(seed) self.array = np.random.rand(nrows, ncols) @fixture(scope='module') def matrix(): return Matrix(nrows=2, ncols=3) def test_nrows(matrix): assert matrix.array.shape[0] == 2 def test_ncols(matrix): assert matrix.array.shape[1] == 3
错误尝试的代码:
from hypothesis import given, strategies @given(seed=strategies.integers()) @fixture(scope='module') def matrix(seed): return Matrix(nrows=2, ncols=3, seed=seed)
结论:不能直接给fixture叠加@given装饰器
@given是Hypothesis用来参数化测试函数的工具,而fixture是pytest的依赖注入机制,两者执行逻辑不兼容。直接叠加会导致pytest无法识别该fixture,因此出现找不到fixture的错误。
替代方案
方案1:给测试类加@given装饰器,共享实例
把需要共用同一个Matrix实例的测试函数放到一个测试类中,给类添加@given装饰器,这样整个类的测试都会使用同一组随机seed生成的实例:
import numpy as np from hypothesis import given, strategies class Matrix: def __init__(self, nrows, ncols, seed=None): np.random.seed(seed) self.array = np.random.rand(nrows, ncols) @given(seed=strategies.integers()) class TestMatrix: def setup_method(self, method, seed): self.matrix = Matrix(nrows=2, ncols=3, seed=seed) def test_nrows(self): assert self.matrix.array.shape[0] == 2 def test_ncols(self): assert self.matrix.array.shape[1] == 3
方案2:fixture中生成单次随机seed(仅单实例共享)
如果不需要多组seed测试,仅需单次随机seed的共享实例,可以在fixture内生成Hypothesis风格的随机seed:
import numpy as np from pytest import fixture from hypothesis import strategies from hypothesis.internal.conjecture import engine class Matrix: def __init__(self, nrows, ncols, seed=None): np.random.seed(seed) self.array = np.random.rand(nrows, ncols) @fixture(scope='module') def matrix(): seed = engine.random_state().draw(strategies.integers()) return Matrix(nrows=2, ncols=3, seed=seed) def test_nrows(matrix): assert matrix.array.shape[0] == 2 def test_ncols(matrix): assert matrix.array.shape[1] == 3
该方案仅生成一组随机seed,无法像@given那样自动探索边界值。
方案3:结合pytest.mark.parametrize与Hypothesis策略
手动生成若干组seed参数,配合pytest的参数化fixture,实现多组seed下的实例共享:
import numpy as np from pytest import fixture, mark from hypothesis import strategies class Matrix: def __init__(self, nrows, ncols, seed=None): np.random.seed(seed) self.array = np.random.rand(nrows, ncols) # 手动生成5组随机seed seeds = [strategies.integers().example() for _ in range(5)] @fixture(scope='module', params=seeds) def matrix(request): return Matrix(nrows=2, ncols=3, seed=request.param) def test_nrows(matrix): assert matrix.array.shape[0] == 2 def test_ncols(matrix): assert matrix.array.shape[1] == 3
该方案支持多组seed测试,但seed数量需手动指定,无法自动进行Hypothesis的智能边界探索。
内容的提问来源于stack exchange,提问作者SimoneGasperini
相关产品推荐
相关产品推荐

