unittest和metaclass:自动test_*方法生成

San*_*nta 4 python unit-testing metaclass

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

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
Run Code Online (Sandbox Code Playgroud)

所以我想以编程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()
Run Code Online (Sandbox Code Playgroud)

我期待一些输出像:

this: False
this: True
that: False
that: True
Run Code Online (Sandbox Code Playgroud)

但我得到的是:

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

OK
Run Code Online (Sandbox Code Playgroud)

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

谢谢,

编辑:修复.请参阅:片段.

unu*_*tbu 5

实际上,这是一个封闭问题:

更改

attrs[testable_name] = lambda self: meth(self, arg)
Run Code Online (Sandbox Code Playgroud)

attrs[testable_name] = lambda self,meth=meth,arg=arg: meth(self, arg)
Run Code Online (Sandbox Code Playgroud)

通过使用默认值,arglambda内部绑定到arg循环的每次迭代期间设置的默认值.如果没有默认值,arg则在arg完成循环的所有迭代后,取最后一个值.(同样的meth).