如何在nose测试中调用生成器并让nose处理?

1 投票
2 回答
714 浏览
提问于 2025-04-17 12:31

我正在尝试创建一个通用的数据驱动测试处理器,这样我就可以在我的nose测试中调用它。我的测试文件大概是这样的:

import ScenarioHandler

def test_foo():
    scenario = ScenarioHandler(__test_foo, [1, 2])
    scenario.run()

def __test_foo(var):
    assert var % 2 == 0, 'Odd!'

ScenarioHandler可能长这样:

class ScenarioHandler(object):

    def __init__(self, test, args):
        self.test = test
        self.args = args

    def run(self):
        for arg in self.args:
            yield self.test, arg

我遇到的问题是,我不知道怎么把ScenarioHandler.run()中的生成器传回nose。我试过在test_foo()中从run()返回生成器,但这样也不行。这真的有可能吗?

2 个回答

0

你为什么不试试这样的写法:

def test_foo():
    for var in [1, 2]:
        yield __test_foo, var

另外,可以看看我在这个回答中提到的add_tests(generator)函数。

0

我不太清楚这个 nose 的东西具体是怎么运作的。

不过你给出的例子是行不通的。

你有一个调用树:

test_foo() -> run()

因为 run() 返回的是一个生成器,而这个生成器在开始迭代之前是不会启动的,所以你需要进行迭代。

你可以这样做:

def test_foo():
    scenario = ScenarioHandler(__test_foo, [1, 2])
    for f, a in scenario.run():
        f(a)

或者更好的是,因为 nose 已经提供了这个功能,可以通过明确测试给定函数的标志来实现,使用 func.func_code.co_flags & CO_GENERATOR != 0

def test_foo():
    scenario = ScenarioHandler(__test_foo, [1, 2])
    for f, a in scenario.run():
        yield f, a

这样 nose 就能进行这些调用。

(其实还有一种更简短的方法,就是使用

test_meth = ScenarioHandler(__test_foo, [1, 2]).run

但可惜的是,这种方法不行,因为方法显然没有被正确识别为有效的调用目标。)

不过你可以在你的类里添加一个“测试生成器”。

# as method:
    def make_test(self):
        def test_generator():
            for item in self.run():
                yield item
        return test_generator

这样你就可以这样做:

test_foo = ScenarioHandler(__test_foo, [1, 2]).make_test()

但可能 nose 提供了更好的方式,通过继承 TestCaseTestSuite 来将你的测试封装在类里。

撰写回答