[TIP] dynamically create tests with unittest

Albert-Jan Roskam fomcl at yahoo.com
Thu Feb 26 12:52:55 PST 2015



----- Original Message -----

> From: Ben Finney <ben+python at benfinney.id.au>
> To: testing-in-python at lists.idyll.org
> Cc: 
> Sent: Thursday, February 26, 2015 1:08 AM
> Subject: Re: [TIP] dynamically create tests with unittest
> 
> Albert-Jan Roskam <fomcl at yahoo.com> writes:
> 
>>  With nose one can use generators to dynamically create tests:
>> 
>>  What is the preferred way to do this with unittest?
> 
> I recommend using the ‘testscenarios’ library
> <URL:https://pypi.python.org/pypi/testscenarios/>:
> 
>     import unittest
>     import testscenarios
> 
>     import system_under_test as sut
> 
>     class foo_func_TestCase(unittest.TestCase, testscenarios.WithScenarios):
>         """ Test cases for the `foo_func` function. 
> """
> 
>         scenarios = [
>                 ('empty baz', {
>                     'baz': [],
>                     'expected_result': "spam",
>                     }),
>                 ('seven baz items', {
>                     'baz': [3, 5, 7, 11, 13, 17, 19],
>                     'expected_result': "beans",
>                     }),
>                 ('default no baz', {
>                     'expected_result': "eggs",
>                     }),
>                 ]
> 
>         def test_returns_expected_result_given_baz(self):
>             """ Should return the expected result given the `baz` 
> value. """
>             baz = getattr(self, 'baz', None)
>             result = sut.foo_func(baz)
>             self.assertEqual(self.expected_result, result)
> 
>         def test_quux_remains_unchanged(self):
>             """ Should not affect the `quuz` value. 
> """
>             quux_prev = sut.quux
>             baz = getattr(self, 'baz', None)
>             sut.foo_func(baz)
>             self.assertEqual(quux_prev, sut.quux)
> 
> That TestCase class will cause six text cases to be generated during the
> test run: one for each combination of scenario and ‘test_*’ function.
> The TestRunner output for each test case will include both the function
> name and the scenario name, to make it clear which condition caused any
> failure.
> 
> Each test case, when created dynamically, will have attributes as
> specified by the name → value mapping for the scenario. Those attributes
> can be used however the test case function chooses.
> 
> Adding new scenarios is then a simple matter of adding new entries in
> the scenario collection for the class; all the test case methods will be
> run with all the scenarios, as distinct test cases in the run.


Hi all,

Thanks very much for your responses.


I am fetching records from a database, and based on metadata and other parameters these records are formatted. For example,  the record would contain floats that represent the number of seconds since the gregorian epoch. I currently test with assertEqual(desired_record, actual_record). Instead of making comparisons by *record*, I would like to generate one test for each *value* to be compared. The following is probably wrong as it is much like writing manually writing the tests, but it illustrates (I hope) what I want to do:

scenarios = [
                ('european date', {
                    'gregorian': 11654150400.0,
                    'fmt': '%d-%m-%Y',  
                    'as_ustring': False, 
                    'expected_result': b'03-02-1952,
                    }),
                ('european datetime, pre 1900', {
                    'gregorian': 0.0,
                    'fmt': '%Y-%m-%d %H:%M:%S',  
                    'as_ustring': False, 
                    'expected_result': b'1582-10-14 00:00:00',
                    }),
                ('american date', {
                    'gregorian': 11654150400.0,

                    'fmt': '%m-%d-%Y',
                    'as_ustring': False,                     'expected_result': b'02-03-1952',
                    }),
                ('iso date', {
                    'gregorian': 11654150400.0,

                                                          'fmt': '%Y-%m-%d', 
                    'as_ustring': False,
                    'expected_result': b'1952-02-03',
                    }),
                ]

I would have expected to have something like (pseudo code):

for desired_value, actual_value, label in zip(desired_record, actual_record, labels):
     generate_test(desired_value, actual_value, label)

Something like that. I hope I am not too vague now. :-)

Regards,
Albert-Jan



More information about the testing-in-python mailing list