views:

273

answers:

2

I wrote a little function that dynamically defines unittest.TestCase classes (trivial version below).

When I moved it out of the same source file into its own module, I can't figure out how to get unittest to discover the new classes. Calling unittest.main() from either file doesn't execute any tests.

factory.py:

import unittest

_testnum = 0
def test_factory(a, b):

    global _testnum

    testname = 'dyntest' + str(_testnum)

    globals()[testname] = type(testname, (unittest.TestCase,), {'testme': lambda self: self.assertEqual(a, b)})

    _testnum += 1


def finish():
    unittest.main()

someotherfile.py:

from factory import test_factory, finish


test_factory(1, 1)
test_factory(1, 2)


if __name__ == '__main__':
    finish()

Output:

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

So it doesn't execute any tests.

Note that keeping it all in the same file works as expected:

import unittest

_testnum = 0
def test_factory(a, b):

    global _testnum

    testname = 'dyntest' + str(_testnum)

    globals()[testname] = type(testname, (unittest.TestCase,), {'testme': lambda self: self.assertEqual(a, b)})

    _testnum += 1


test_factory(1, 1)
test_factory(1, 2)

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

Output (as expected):

.F
======================================================================
FAIL: testme (__main__.dyntest1)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "partb.py", line 11, in <lambda>
    globals()[testname] = type(testname, (unittest.TestCase,), {'testme': lambda self: self.assertEqual(a, b)})
AssertionError: 1 != 2

----------------------------------------------------------------------
Ran 2 tests in 0.008s

FAILED (failures=1)

How I use my test_factory() function such that I can execute all of the TestCase objects it defines from a separate source file?

+5  A: 

The general idea (what unittest.main does for you) is:

suite = unittest.TestLoader().loadTestsFromTestCase(SomeTestCase)
unittest.TextTestRunner(verbosity=2).run(suite)

as per http://docs.python.org/library/unittest.html?highlight=unittest#module-unittest . Your test cases are hidden in globals() by the test_factory function, so just do a dir(), find the globals that are instances of unittest.TestCase (or ones with names starting with 'dyntest', etc), and just build your suite that way and run it.

Alex Martelli
+4  A: 

By default, unittest.main() looks for unit TestCase objects in the main module. The test_factory creates the TestCase objects in its own module. That's why moving it outside of the main module causes the behavior you see.

Try:

def finish():
    unittest.main(module=__name__)
Don
-1: No reference to the documentation.
S.Lott