unittests with different parameters

Jonathan Hartley tartley at tartley.com
Tue Nov 23 09:00:43 EST 2010


On Nov 22, 11:38 am, Ulrich Eckhardt <ulrich.eckha... at dominolaser.com>
wrote:
> Hi!
>
> I'm writing tests and I'm wondering how to achieve a few things most
> elegantly with Python's unittest module.
>
> Let's say I have two flags invert X and invert Y. Now, for testing these, I
> would write one test for each combination. What I have in the test case is
> something like this:
>
>   def test_invert_flags(self):
>       """test flags to invert coordinates"""
>       tests = [((10, 20), INVERT_NONE, (10, 20)),
>                ((10, 20), INVERT_X, (-10, 20)),
>                ((10, 20), INVERT_Y, (10, -20))]
>       for input, flags, expected in tests:
>           res = do_invert(input, flags)
>           self.assertEqual(res, expected,
>                            "%s caused wrong results" % (flags,))
>
> So, what I do that I test the function 'do_invert' for different input
> combinations and verify the result. The ugly thing is that this will abort
> the whole test if one of the tests in the loop fails. So, my question is
> how do I avoid this?
>
> I know that I could write a common test function instead:
>
>   def _test_invert_flags(self, input, flags, expected):
>       res = do_invert(input, flags)
>       self.assertEqual(res, expected)
>
>   def test_invert_flags_non(self):
>       """test not inverting coordinates"""
>       self._test_invert_flags((10, 20), INVERT_NONE, (10, 20))
>
>   def test_invert_flags_x(self):
>       """test inverting X coordinates"""
>       self._test_invert_flags((10, 20), INVERT_X, (-10, 20))
>
>   def test_invert_flags_y(self):
>       """test inverting Y coordinates"""
>       self._test_invert_flags((10, 20), INVERT_Y, (10, -20))
>
> What I don't like here is that this is unnecessarily verbose and that it
> basically repeats information. Also, I'd rather construct the error message
> from the data instead of maintaining it in different places, because
> manually keeping those in sync is another, errorprone burden.
>
> Any suggestions?
>
> Uli
>
> --
> Domino Laser GmbH
> Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932


The following is a bit ghastly, I'm not sure I'd recommend it, but if
you are determined, you could try dynamically adding test methods to
the test class. The following is untested - I suspect I have made a
schoolboy error in attempting to make methods out of functions - but
something like it might work:


class MyTestClass(unittest.TestCase):
  pass

testdata = [
  (INPUTS, EXPECTED),
  (INPUTS, EXPECTED),
  (INPUTS, EXPECTED),
]

for index, (input, expected) in enumerate(testdata):
    # the following sets an attribute on MyTestClass
    # the names of the attributes are 'test_1', 'test_2', etc
    # the value of the attributes is a test method that performs the
assert
    setattr(
        MyTestClass,
        'test_%d' % (index,),
        lambda s: s.assertEquals(METHOD_UNDER_TEST(*input), expected)
    )



More information about the Python-list mailing list