Unittest - How do I code lots of simple tests

Duncan Booth duncan at NOSPAMrcp.co.uk
Wed Oct 22 04:29:17 EDT 2003


Paul Moore <pf_moore at yahoo.co.uk> wrote in news:brsawdmh.fsf at yahoo.co.uk:

> But I can't really see that as the "right approach".
> 
> Can anyone suggest a more reasonable way of running this sort of
> table-driven test via unittest?

Ok, how about the file below.
It uses a metaclass to generate dynamic tests from a table. I deliberately 
wrote the tests so that one fails, when run it will tell you that 
test_roman_v failed so you can see that the names get handled properly.

Output:
D:\temp>tabletest.py --verbose
testMe (__main__.MyTests) ... ok
test_roman_i (__main__.MyTests) ... ok
test_roman_v (__main__.MyTests) ... FAIL
test_roman_x (__main__.MyTests) ... ok

======================================================================
FAIL: test_roman_v (__main__.MyTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\temp\tabletest.py", line 21, in test
    self.assertEquals(roman, 'x')
  File "D:\Python23\lib\unittest.py", line 302, in failUnlessEqual
    raise self.failureException, \
AssertionError: 'v' != 'x'

----------------------------------------------------------------------
Ran 4 tests in 0.020s

FAILED (failures=1)


Note that the metaclass itself is reusable. The factory 
function tableDrivenTests, although defined within the class is a function, 
not a method, and cannot access any members of the class (since the class 
does not yet exist at the time it is called). The table itself either has 
to be created inside the tableDrivenTests function, or global.
The factory function simply returns a dictionary of functions which are 
added to the class, note that the keyname in the dictionary is important so 
far as the unittest code is concerned, not the original name of the 
function.

Also be sure to pass parameters into the test function using default 
parameters as nested scopes will get the values left at the end of the loop 
(so you might end up with lots of tests that all do the same thing). I 
mention that here because I did exactly that writing the code.

---- begin tabletest.py ----
class MetaTableTest(type):
    def __new__(metacls, name, bases, dict):
        factory = dict['tableDrivenTests']
        dict.update(factory())
        return super(MetaTableTest, metacls).__new__(metacls, name, bases, 
dict)

import unittest

class MyTests(unittest.TestCase):
    __metaclass__ = MetaTableTest

    
    def tableDrivenTests():
        '''Return a dictionary of additional test functions'''
        knownValues = (1,'i'), (5, 'v'), (10, 'x')
        table = {}
        for arabic, roman in knownValues:
            def test(self, arabic=arabic, roman=roman):
                if arabic==1:
                    self.assertEquals(roman, 'i')
                else:
                    self.assertEquals(roman, 'x')

            table['test_roman_%s' % roman] = test
        return table

    def testMe(self):
        self.assert_(True)


if __name__=='__main__':
    unittest.main()
    
---- end tabletest.py ----

-- 
Duncan Booth                                             duncan at rcp.co.uk
int month(char *p){return(124864/((p[0]+p[1]-p[2]&0x1f)+1)%12)["\5\x8\3"
"\6\7\xb\1\x9\xa\2\0\4"];} // Who said my code was obscure?




More information about the Python-list mailing list