Pytest-将多个灯具参数组合到单个灯具中,以优化灯具实例化

时间:2020-04-09 04:07:39

标签: python unit-testing pytest fixtures

我有一个现有的pytest测试,该测试使用一些预定义的列表来测试所有这些列表的叉积:

A_ITEMS = [1, 2, 3]
B_ITEMS = [4, 5, 6]
C_ITEMS = [7, 8, 9]

我还有一个昂贵的灯具,其内部条件取决于A和B项(而不是C),称为F:

class Expensive:
    def __init__(self):
        # expensive set up
        time.sleep(10)
    def check(self, a, b, c):
        return True  # keep it simple, but in reality this depends on a, b and c

@pytest.fixture
def F():
    return Expensive()

目前,我有一种幼稚的方法,可以简单地将测试函数参数化:

@pytest.mark.parametrize("A", A_ITEMS)
@pytest.mark.parametrize("B", B_ITEMS)
@pytest.mark.parametrize("C", C_ITEMS)
def test_each(F, A, B, C):
    assert F.check(A, B, C)

这将测试F与A,B和C项的所有组合,但是它通过Expensive夹具为每次测试构造了一个新的F实例。更具体地说,它通过夹具F 为A,B和C的每种组合重建一个新的Expensive

这是非常低效的,因为当A和B的值发生变化时,我只需要构造一个新的Expensive,而在所有C测试中它们都不是。

我想做的是以某种方式将F固定装置与A_ITEMSB_ITEMS列表组合在一起,以便F固定装置每次对实例运行一次仅实例化一个新实例。 C的值。

我的第一种方法是将A和B列表分成各自的固定装置,然后将它们与F固定装置组合在一起:

class Expensive:
    def __init__(self, A, B):
        # expensive set up
        self.A = A
        self.B = B
        time.sleep(10)
    def check(self, c):
        return True  # keep it simple

@pytest.fixture(params=[1,2,3])
def A(request):
    return request.param

@pytest.fixture(params=[4,5,6])
def B(request):
    return request.param

@pytest.fixture
def F(A, B):
    return Expensive(a, b)

@pytest.mark.parametrize("C", C_ITEMS)
def test_each2(F, C):
    assert F.check(C)

尽管这会测试所有组合,但不幸的是,这会为每个测试创建Expensive的新实例,而不是将A和B的每个项目组合到一个可以为C的每个值重用的实例中。

我研究了间接灯具,但看不到将多个列表(即A和B项)发送到单个灯具的方法。

我可以使用pytest采取更好的方法吗?本质上,我想做的是使Expensive的实例化次数最少,因为它取决于项A和B的值。

注意:我已经尝试简化此过程,但是实际情况是F代表创建新进程,A和B是该进程的命令行参数,而C只是传递给通过套接字进行处理。因此,我希望能够将C的每个值发送给该进程,而不必每次C更改时都重新创建它,但是很明显,如果A或B更改,我需要重新启动它(因为它们是该进程的命令行参数)。 / p>

2 个答案:

答案 0 :(得分:0)

如果不选择使用pytest范围(如其他答案所述),则可以尝试缓存扩展对象,以便仅在需要时才构造它。

基本上,这扩展了问题中给出的建议,并为最后使用的参数提供了额外的静态缓存,以避免在不需要时创建新的Expansive

@pytest.fixture(params=A_ITEMS)
def A(request):
    return request.param


@pytest.fixture(params=B_ITEMS)
def B(request):
    return request.param


class FFactory:
    lastAB = None
    lastF = None

    @classmethod
    def F(cls, A, B):
        if (A, B) != cls.lastAB:
            cls.lastAB = (A, B)
            cls.lastF = Expensive(A, B)
        return cls.lastF

@pytest.fixture
def F(A, B):
    return FFactory.F(A, B)


@pytest.mark.parametrize("C", C_ITEMS)
def test_each(F, C):
    assert F.check(C)

答案 1 :(得分:0)

在这种情况下,我已经成功地使用了范围更广的灯具(模块或会话)作为每次测试灯具的“缓存”,这种情况下,灯具的“寿命”无法正确对齐摊销费用或其他费用。