# Example usage

DDT consists of a class decorator `ddt` (for your `TestCase` subclass) and two method decorators (for your tests that want to be multiplied):

- `data`: contains as many arguments as values you want to feed to the test.
- `file_data`: will load test data from a JSON or YAML file.

Note

Only files ending with ”.yml” and ”.yaml” are loaded as YAML files. All other files are loaded as JSON files.

Normally each value within `data` will be passed as a single argument to your test method. If these values are e.g. tuples, you will have to unpack them inside your test. Alternatively, you can use an additional decorator, `unpack`, that will automatically unpack tuples and lists into multiple arguments, and dictionaries into multiple keyword arguments. See examples below.

This allows you to write your tests as:

    import unittest
    from ddt import ddt, data, file_data, unpack
    from test.mycode import larger_than_two, has_three_elements, is_a_greeting

    try:
        import yaml
    except ImportError:  # pragma: no cover
        have_yaml_support = False
    else:
        have_yaml_support = True
        del yaml

    # A good-looking decorator
    needs_yaml = unittest.skipUnless(
        have_yaml_support, "Need YAML to run this test"
    )

    class Mylist(list):
        pass

    def annotated(a, b):
        r = Mylist([a, b])
        setattr(r, "__name__", "test_%d_greater_than_%d" % (a, b))
        return r

    @ddt
    class FooTestCase(unittest.TestCase):
        def test_undecorated(self):
            self.assertTrue(larger_than_two(24))

        @data(3, 4, 12, 23)
        def test_larger_than_two(self, value):
            self.assertTrue(larger_than_two(value))

        @data(1, -3, 2, 0)
        def test_not_larger_than_two(self, value):
            self.assertFalse(larger_than_two(value))

        @data(annotated(2, 1), annotated(10, 5))
        def test_greater(self, value):
            a, b = value
            self.assertGreater(a, b)

        @file_data("test_data_dict_dict.json")
        def test_file_data_json_dict_dict(self, start, end, value):
            self.assertLess(start, end)
            self.assertLess(value, end)
            self.assertGreater(value, start)

        @file_data('test_data_dict.json')
        def test_file_data_json_dict(self, value):
            self.assertTrue(has_three_elements(value))

        @file_data('test_data_list.json')
        def test_file_data_json_list(self, value):
            self.assertTrue(is_a_greeting(value))

        @needs_yaml
        @file_data("test_data_dict_dict.yaml")
        def test_file_data_yaml_dict_dict(self, start, end, value):
            self.assertLess(start, end)
            self.assertLess(value, end)
            self.assertGreater(value, start)

        @needs_yaml
        @file_data('test_data_dict.yaml')
        def test_file_data_yaml_dict(self, value):
            self.assertTrue(has_three_elements(value))

        @needs_yaml
        @file_data('test_data_list.yaml')
        def test_file_data_yaml_list(self, value):
            self.assertTrue(is_a_greeting(value))

        @data((3, 2), (4, 3), (5, 3))
        @unpack
        def test_tuples_extracted_into_arguments(self, first_value, second_value):
            self.assertTrue(first_value > second_value)

        @data([3, 2], [4, 3], [5, 3])
        @unpack
        def test_list_extracted_into_arguments(self, first_value, second_value):
            self.assertTrue(first_value > second_value)

        @unpack
        @data({'first': 1, 'second': 3, 'third': 2},
              {'first': 4, 'second': 6, 'third': 5})
        def test_dicts_extracted_into_kwargs(self, first, second, third):
            self.assertTrue(first < third < second)

        @data(u'ascii', u'non-ascii-\N{SNOWMAN}')
        def test_unicode(self, value):
            self.assertIn(value, (u'ascii', u'non-ascii-\N{SNOWMAN}'))

        @data(3, 4, 12, 23)
        def test_larger_than_two_with_doc(self, value):
            """Larger than two with value {0}"""
            self.assertTrue(larger_than_two(value))

        @data(3, 4, 12, 23)
        def test_doc_missing_args(self, value):
            """Missing args with value {0} and {1}"""
            self.assertTrue(larger_than_two(value))

        @data(3, 4, 12, 23)
        def test_doc_missing_kargs(self, value):
            """Missing kargs with value {value} {value2}"""
            self.assertTrue(larger_than_two(value))

        @data([3, 2], [4, 3], [5, 3])
        @unpack
        def test_list_extracted_with_doc(self, first_value, second_value):
            """Extract into args with first value {} and second value {}"""
            self.assertTrue(first_value > second_value)

Where `test_data_dict.json`:

    {
        "unsorted_list": [ 10, 12, 15 ],
        "sorted_list": [ 15, 12, 50 ]
    }

and `test_data_list.json`:

    [
        "Hello",
        "Goodbye"
    ]

    unsorted_list:
      - 10
      - 15
      - 12

    sorted_list: [ 15, 12, 50 ]

and `test_data_list.yaml`:

    - "Hello"
    - "Goodbye"

And then run them with your favourite test runner, e.g. if you use nose:

    $ nosetests -v test/test_example.py

The number of test cases actually run and reported separately has been multiplied.

DDT will try to give the new test cases meaningful names by converting the data values to valid python identifiers.

Note

Python 2.7.3 introduced hash randomization which is by default enabled on Python 3.3 and later. DDT’s default mechanism to generate meaningful test names will not use the test data value as part of the name for complex types if hash randomization is enabled.

You can disable hash randomization by setting the `PYTHONHASHSEED` environment variable to a fixed value before running tests (`export PYTHONHASHSEED=1` for example).