Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
202 views
in Technique[技术] by (71.8m points)

python - nose, unittest.TestCase and metaclass: auto-generated test_* methods not discovered

This is a follow-up question for unittest and metaclass: automatic test_* method generation:

For this (fixed) unittest.TestCase layout:

#!/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}_v4,6(self)
            for suffix, arg in (('_false', False), ('_true', True)):
                testable_name = 'test{0}{1}'.format(testname, suffix)
                testable = lambda self, func=meth, arg=arg: func(self, arg)
                attrs[testable_name] = testable

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


class TestCase(unittest.TestCase):

    __metaclass__ = TestMaker

    def test_normal(self):
        print 'Hello from ' + self.id()

    def _test_this(self, arg):
        print '[{0}] this: {1}'.format(self.id(), str(arg))

    def _test_that(self, arg):
        print '[{0}] that: {1}'.format(self.id(), str(arg))


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

This works using stdlib's framework. Expected and actual output:

C:Userssanta4ntDesktop>C:Python27python.exe test_meta.py
Hello from __main__.TestCase.test_normal
.[__main__.TestCase.test_that_false] that: False
.[__main__.TestCase.test_that_true] that: True
.[__main__.TestCase.test_this_false] this: False
.[__main__.TestCase.test_this_true] this: True
.
----------------------------------------------------------------------
Ran 5 tests in 0.015s

OK

However, since I am actually using nose, this trick seems to not agree with it. The output I got is:

C:Userssanta4ntDesktop>C:Python27python.exe C:Python27Scripts
osetests test_meta.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

In short, the test_* methods generated by the metaclass do not register with nose. Can anyone shed a light on this?

Thanks,

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

So, after sleuthing through both stdlib's unittest and nose's loader and selector source code, it turns out that nose overrides unittest.TestLoader.getTestCaseNames to use its own selector (with plugin points).

Now, nose's selector looks for a potential method's method.__name__ to match certain regexes, black and white lists, and plugins' decisions.

In my case, the dynamically generated functions have its testable.__name__ == '<lambda>', matching none of nose's selector criteria.

To fix,

        # inject methods: test{testname}_v4,6(self)
        for suffix, arg in (('_false', False), ('_true', True)):
            testable_name = 'test{0}{1}'.format(testname, suffix)
            testable = lambda self, arg=arg: meth(self, arg)
            testable.__name__ = testable_name    # XXX: the fix
            attrs[testable_name] = testable

And sure enough:

(sandbox-2.7)bash-3.2$ nosetests -vv 
test_normal (test_testgen.TestCase) ... ok
test_that_false (test_testgen.TestCase) ... ok
test_that_true (test_testgen.TestCase) ... ok
test_this_false (test_testgen.TestCase) ... ok
test_this_true (test_testgen.TestCase) ... ok

----------------------------------------------------------------------
Ran 5 tests in 0.005s

OK

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...