在Python中生成py.test测试

23 投票
3 回答
12755 浏览
提问于 2025-04-16 11:23

先问问题,感兴趣的话再看解释。

在使用py.test的情况下,我该如何从一小部分测试函数模板生成大量的测试函数呢?

就像这样:

models = [model1,model2,model3]
data_sets = [data1,data2,data3]

def generate_test_learn_parameter_function(model,data):
    def this_test(model,data):
        param = model.learn_parameters(data)
        assert((param - model.param) < 0.1 )
    return this_test

for model,data in zip(models,data_sets):
    # how can py.test can see the results of this function?
    generate_test_learn_parameter_function(model,data)

解释:

我写的代码是用来处理一个模型结构、一些数据,并学习模型的参数。所以我的单元测试包括一堆模型结构和预先生成的数据集,然后对每个结构和数据进行大约5个机器学习任务的测试。

如果我手动编写这些测试,就需要为每个模型和每个任务写一个测试。每当我想出一个新模型时,就得复制粘贴这5个任务,还得改动我指向的结构和数据。这让我觉得这样做不太好。理想情况下,我希望有5个模板函数来定义我的5个任务,然后只需为我指定的模型列表生成测试函数。

我在网上搜索,发现要么是a) 工厂模式,要么是b) 闭包,这两者让我感到困惑,并让我觉得应该有更简单的方法,因为这个问题应该是很多程序员都会遇到的。那么,真的有吗?


编辑:这是解决这个问题的方法!

def pytest_generate_tests(metafunc):
    if "model" in metafunc.funcargnames:
        models = [model1,model2,model3]
        for model in models:
            metafunc.addcall(funcargs=dict(model=model))

def test_awesome(model):
    assert model == "awesome"

这将把 test_awesome 测试应用到我模型列表中的每个模型上!谢谢 @dfichter!

(注意:那个断言总是会通过,顺便说一下)

3 个回答

1

现在最简单的解决办法就是使用 pytest.mark.parametrize 这个功能:

@pytest.mark.parametrize('model', ["awesome1", "awesome2", "awesome3"])
def test_awesome(model):
    assert model.startswith("awesome")
8

你也可以使用参数化的固定装置来实现这个功能。简单来说,hooks是一个用来为Py.test构建插件的接口,而参数化的固定装置是一种通用的方法,可以让你创建一个固定装置,输出多个值,并为这些值生成额外的测试案例。

插件通常是一些在整个项目(或包)中都能用的功能,而不是针对某个特定测试案例的功能。参数化的固定装置正是用来为测试案例参数化某些资源所需要的。

所以你的解决方案可以改写成这样:

@pytest.fixture(params=[model1, model2, model3])
def model(request):
    return request.param

def test_awesome(model):
    assert model == "awesome"
20

很好的直觉。py.test 确实支持你所说的功能,它有一个叫 pytest_generate_tests() 的钩子。你可以在 这里 找到详细的解释。

撰写回答