【问题标题】:unittest and metaclass: automatic test_* method generationunittest 和 metaclass:自动 test_* 方法生成
【发布时间】:2011-07-07 18:01:52
【问题描述】:

当我为框架创建测试时,我开始注意到以下模式:

class SomeTestCase(unittest.TestCase):

    def test_feat_true(self):
        _test_feat(self, True)

    def test_feat_false(self):
        _test_feat(self, False)

    def _test_feat(self, arg):
        pass    # test logic goes here

所以我想通过元类以编程方式为这些类型的测试类创建test_feat_* 方法。换句话说,对于每个带有签名_test_{featname}(self, arg) 的私有方法,我想要创建两个带有签名test_{featname}_true(self)test_{featname}_false(self) 的顶级可发现方法。

我想出了类似的东西:

#!/usr/bin/env python

import unittest


class TestMaker(type):

    def __new__(cls, name, bases, attrs):
        callables = dict([
            (meth_name, meth) for (meth_name, meth) in attrs.items() if
            meth_name.startswith('_test')
        ])

        for meth_name, meth in callables.items():
            assert callable(meth)
            _, _, testname = meth_name.partition('_test')

            # inject methods: test{testname}_{[false,true]}(self)
            for suffix, arg in (('false', False), ('true', True)):
                testable_name = 'test{0}{1}'.format(testname, suffix)
                attrs[testable_name] = lambda self: meth(self, arg)

        return type.__new__(cls, name, bases, attrs)


class TestCase(unittest.TestCase):

    __metaclass__ = TestMaker

    def _test_this(self, arg):
        print 'this: ' + str(arg)

    def _test_that(self, arg):
        print 'that: ' + str(arg)


if __name__ == '__main__':
    unittest.main()

我希望有一些输出,例如:

this: False
this: True
that: False
that: True

但我得到的是:

$ ./test_meta.py
that: True
.that: True
.that: True
.that: True
.
----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK

看起来我缺少一些关闭规则。我该如何解决这个问题?有更好的方法吗?

谢谢,

编辑:已修复。请参阅:the snippet

【问题讨论】:

    标签: python unit-testing metaclass


    【解决方案1】:

    确实是闭包问题:

    改变

    attrs[testable_name] = lambda self: meth(self, arg)
    

    attrs[testable_name] = lambda self,meth=meth,arg=arg: meth(self, arg)
    

    通过使用默认值,lambda 内的arg 绑定到循环的每次迭代期间设置的默认值arg。如果没有默认值,arg 在循环的所有迭代完成后将采用最后一个值arg。 (meth 也是如此)。

    【讨论】:

    • 嗯,其实meth函数也承担了迭代的最后一个函数。使用相同的技巧即可修复。
    【解决方案2】:

    我会考虑使用鼻子测试生成器来处理这类事情,而不是走元类路线:

    http://somethingaboutorange.com/mrl/projects/nose/1.0.0/writing_tests.html#test-generators

    测试生成器的缺点是它们是特定于鼻子的功能,因此您需要在 stdlib 之外引入依赖项。好处是我认为它们更容易编写和理解。

    【讨论】:

    • 好吧,你知道什么。我用鼻子!我将其提炼为裸 stdlib 代码,以显示我要解决的问题。不过,我不知道鼻子上有这样的特征。我会检查一下。谢谢!
    • 啊,爆炸!注意:“请注意,unittest.TestCase 子类不支持方法生成器。”我需要依赖在这些子类的树中定义的固定装置。
    猜你喜欢
    • 2011-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-23
    • 1970-01-01
    • 2016-01-04
    相关资源
    最近更新 更多