name lookup failure using metaclasses with unittests

Peter Otten __peter__ at web.de
Wed Apr 10 05:52:56 EDT 2013


Ulrich Eckhardt wrote:

> Hi!
> 
> I'm having problems using a metaclass to generate test functions. This
> works when I try to run all tests from the module or test case, but it
> fails when I'm trying to specify a single test function. My environment
> is Python 2.7.3 on MS Windows 7 at the moment. It should be upgraded to
> at least 2.7.4 or better to 3, but see the notes on Python 3 below.
> 
> # my_module.py
> import unittest
> class X(unittest.TestCase):
>      def __metaclass__(name, bases, dict):
>          # attach function
>          def test(self):
>              pass
>          dict['test_1'] = test
>          dict['test_2'] = test
>          # create class
>          return type(name, bases, dict)
> 
> The error when I'm trying to run "python -m unittest my_module.X.test_1"
> is: "Value error: no such test method in <class 'my_module.X'>: test".
> The astonishing part is that it claims that "test" is not found while I
> asked it to run "test_1". The name it complains about is the name of the
> function inside the metaclass function. In all other cases, like e.g.
> giving "-v" it reports the correct function name. My question here is
> whether I'm doing something wrong or whether I discovered a bug.

Here's a simpler demo of the problem:

$ cat tmp.py
import unittest

class X(unittest.TestCase):
    def test_1(self): pass
    test_1.__name__ = "test_2"

$ python -m unittest -v tmp
test_1 (tmp.X) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.001s

OK
$ python -m unittest -v tmp.X.test_1
Traceback (most recent call last):
  File "/usr/lib/python2.7/runpy.py", line 162, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
    exec code in run_globals
  File "/usr/lib/python2.7/unittest/__main__.py", line 12, in <module>
    main(module=None)
  File "/usr/lib/python2.7/unittest/main.py", line 94, in __init__
    self.parseArgs(argv)
  File "/usr/lib/python2.7/unittest/main.py", line 149, in parseArgs
    self.createTests()
  File "/usr/lib/python2.7/unittest/main.py", line 158, in createTests
    self.module)
  File "/usr/lib/python2.7/unittest/loader.py", line 128, in 
loadTestsFromNames
    suites = [self.loadTestsFromName(name, module) for name in names]
  File "/usr/lib/python2.7/unittest/loader.py", line 109, in 
loadTestsFromName
    return self.suiteClass([parent(obj.__name__)])
  File "/usr/lib/python2.7/unittest/case.py", line 191, in __init__
    (self.__class__, methodName))
ValueError: no such test method in <class 'tmp.X'>: test_2

It looks like this particular invocation relies on class attribute and 
function __name__ being identical.

Please file a bug report.

> Now, concerning Python 3, it fails to detect any test case at all! My
> guess is that the unittest library was changed to use metaclasses itself
> in order to detect classes derived from unittest.TestCase. Therefore,
> overriding the metaclass breaks test case discovery. My question in that
> context is how do I extend metaclasses instead of overriding it? In
> other words, what is the equivalent to super() for class creation?

Python 3 does not recognize the __metaclass__ attribute as the metaclass. 
You need to provide it like so:

def __metaclass__(name, bases, dict):
    ...

class X(unittest.TestCase, metaclass=__metaclass__):
    pass





More information about the Python-list mailing list