Python unittest: Generate multiple tests programmatically?

Nick Heiner picture Nick Heiner · May 9, 2010 · Viewed 30.9k times · Source

Possible Duplicate:
How to generate dynamic (parametrized) unit tests in python?

I have a function to test, under_test, and a set of expected input/output pairs:

[
(2, 332),
(234, 99213),
(9, 3),
# ...
]

I would like each one of these input/output pairs to be tested in its own test_* method. Is that possible?

This is sort of what I want, but forcing every single input/output pair into a single test:

class TestPreReqs(unittest.TestCase):

    def setUp(self):
        self.expected_pairs = [(23, 55), (4, 32)]

    def test_expected(self):
        for exp in self.expected_pairs:
            self.assertEqual(under_test(exp[0]), exp[1])

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

(Also, do I really want to be putting that definition of self.expected_pairs in setUp?)

UPDATE: Trying doublep's advice:

class TestPreReqs(unittest.TestCase):

    def setUp(self):
        expected_pairs = [
                          (2, 3),
                          (42, 11),
                          (3, None),
                          (31, 99),
                         ]

        for k, pair in expected_pairs:
            setattr(TestPreReqs, 'test_expected_%d' % k, create_test(pair))

    def create_test (pair):
        def do_test_expected(self):
            self.assertEqual(get_pre_reqs(pair[0]), pair[1])
        return do_test_expected


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

This does not work. 0 tests are run. Did I adapt the example incorrectly?

Answer

Rory picture Rory · Sep 22, 2010

I had to do something similar. I created simple TestCase subclasses that took a value in their __init__, like this:

class KnownGood(unittest.TestCase):
    def __init__(self, input, output):
        super(KnownGood, self).__init__()
        self.input = input
        self.output = output
    def runTest(self):
        self.assertEqual(function_to_test(self.input), self.output)

I then made a test suite with these values:

def suite():
    suite = unittest.TestSuite()
    suite.addTests(KnownGood(input, output) for input, output in known_values)
    return suite

You can then run it from your main method:

if __name__ == '__main__':
    unittest.TextTestRunner().run(suite())

The advantages of this are:

  • As you add more values, the number of reported tests increases, which makes you feel like you are doing more.
  • Each individual test case can fail individually
  • It's conceptually simple, since each input/output value is converted into one TestCase