Iterating over test data in unit tests

Ben Finney bignose+hates-spam at benfinney.id.au
Tue Dec 6 08:50:46 CET 2005


Ben Finney <bignose+hates-spam at benfinney.id.au> wrote:
> Summary: I'm looking for idioms in unit tests for factoring out
> repetitive iteration over test data.

Thanks to those who've offered suggestions, especially those who
suggested I look at generator functions. This leads to::

    import unittest

    import bowling      # Module to be tested

    class Test_Game(unittest.TestCase):
        """ Test case for the Game class """

        def setUp(self):
            """ Set up test fixtures """
            self.game_data = {
                'none': dict(score=0, throws=[], frame=1),
                'one': dict(score=5, throws=[5], frame=1),
                'two': dict(score=9, throws=[5, 4], frame=2),
                'three': dict(score=14, throws=[5, 4, 5], frame=2),
                'strike': dict(score=26, throws=[10, 4, 5, 7], frame=3),
            }

            self.game_params = {}
            for key, dataset in self.game_data.items():
                params = {}
                instance = bowling.Game()
                params['instance'] = instance
                params['dataset'] = dataset
                self.game_params[key] = params

        def iterate_params(test_params=None):
            """ Yield the test parameters """
            if not test_params:
                test_params = self.game_params
            for key, params in test_params.items():
                dataset = params['dataset']
                instance = params['instance']
                yield key, dataset, instance

        def test_score_throws(self):
            """ Game score should be calculated from throws """
            for key, dataset, instance in self.iterate_params():
                score = dataset['score']
                for throw in dataset['throws']:
                    instance.add_throw(throw)
                self.failUnlessEqual(score, instance.get_score())

        def test_current_frame(self):
            """ Current frame should be as expected """
            for key, dataset, instance in self.iterate_params():
                frame = dataset['frame']
                for throw in dataset['throws']:
                    instance.add_throw(throw)
                self.failUnlessEqual(frame, instance.current_frame)

That's much better. Each test is now clearly about looping through the
datasets, but the infrastructure to do so is factored out. Adding a
test case modelled on the existing cases just means adding a new entry
to the game_data dictionary. Setting up a different kind of test --
e.g. for invalid game data -- just means setting up a new params
dictionary and feeding that to the same generator function.

I like it. Can it be improved? Are there readability problems that can
be fixed? Is the test fixture setup too complex? Should the iterator
become even more general, and be refactored out to a test framework
for the project?

-- 
 \     "Those who can make you believe absurdities can make you commit |
  `\                                         atrocities."  -- Voltaire |
_o__)                                                                  |
Ben Finney



More information about the Python-list mailing list