[Python-ideas] Minor suggestion for unittest

Terry Jones terry at jon.es
Thu Apr 26 02:52:16 CEST 2007


There's a simple change that could be made to unittest that would make it
easier to automate some forms of testing.

I want to be able to dynamically add tests to an instance of a class
derived from unittest.TestCase. There are occasions when I don't want to
write my tests upfront in a Python file. E.g., given a bunch of
test/expectedResult data sitting around (below in a variable named
MyTestData), it would be nice to be able to do the following (untested
here, but I did it earlier for real and it works fine):

    import unittest

    class Test(unittest.TestCase):
        def runTest(): pass

    suite = unittest.TestSuite()

    for testFunc, expectedResult in MyTestData:
        newTestFuncName = 'dynamic-test-' + testFunc.__name__
        def tester():
            self.assertEqual(testFunc(), expectedResult)
        test = Test()
        setattr(test, newTestFuncName, tester)
        # Set the class instance up so that it will be the one run.
        test.__init__(newTestFuncName)  # ugh!
        suite.addTest(test)

    suite.run()

The explicit call to __init__ (marked ugh!) is ugly, dangerous, etc. You
could also say test._testMethodName = newTestFuncName (and set
_testMethodDoc too), but that's also ugly.


This would all be very simple though if instead of starting out like:

    class TestCase:
        def __init__(self, methodName='runTest'):
            try:
                self._testMethodName = methodName
                testMethod = getattr(self, methodName)
                self._testMethodDoc = testMethod.__doc__
            except AttributeError:
                raise ValueError, "no such test method in %s: %s" % \
                      (self.__class__, methodName)

unittest.TestCase started out like this:

    class TestCase:
        def __init__(self, methodName='runTest'):
            self.setTestMethod(methodName)

        def setTestMethod(self, methodName):
            try:
                self._testMethodName = methodName
                testMethod = getattr(self, methodName)
                self._testMethodDoc = testMethod.__doc__
            except AttributeError:
                raise ValueError, "no such test method in %s: %s" % \
                      (self.__class__, methodName)


That would allow people to create an instance of their Test class, add a
method to it using setattr, and then use setTestMethod to set the method
to be run.

A further improvement would be to have _testMethodName be None or left
undefined (and accessed via __getattr__) for as long as possible rather
than being set to runTest (and looked up with getattr) immediately. That
would allow the removal of the do-nothing runTest method in the above. No
old code need be broken as runTest would still be the default. You'd just
have a chance to get in there earlier so it never saw the light of day.

Programmers like to automate things, especially testing. These changes
don't break any existing code but they allow additional test automation.
Of course you _could_ achieve the above by writing out a brand new temp.py
file, running it, and so on, but that's not very Pythonic, is a bunch more
work, needs cleanup (temp.py needs to go away), etc.

I have some further thoughts about how to make this a bit more flexible,
but I'll save those for later, supposing there's any interest in the above.

Terry Jones



More information about the Python-ideas mailing list