Python data-driven tool: DDT

 

background

python's unittest does not have its own data-driven function.

Therefore, if you use unittest and want to use data-driven, you can use DDT to complete it.

DDT is the abbreviation of "data driven tests".

Information: http://ddt.readthedocs.io/en/latest/

usage method

dd.ddt:

Decoration class, that is, the class inherited from TestCase.

ddt.data:

Decoration test method. Parameters are a series of values.

ddt.file_data:

Decoration test method. The parameter is the file name. The file can be of json or yaml type.

Note that if the file ends with ". yml" or ". Yaml", ddt will be treated as yaml type, and all other files will be treated as json files.

If there are lists in the file, the values of each list will be displayed as test case parameters and as test case method name suffixes.

If there is a dictionary in the file, the key of the dictionary will be displayed as the suffix of the test case method, and the value of the dictionary will be used as the test case parameter.

ddt.unpack:

Used when passing complex data structures. For example, using tuples or lists, after adding unpack, ddt will automatically map tuples or lists to multiple parameters. Dictionaries can also be handled like this. See example 2 below

Test case method name generation rule

After using ddt, a new test case method name will be generated: the previous test case method name_ ordinal_data

Previous test case method name: the defined test case method name. For example, def test_large(), here is test_large

ordinal: integer, incremented from 1.

Data: if the passed data exists__ name__ Property, here is the name of the data__ name__ Value. If not defined__ name__ Property, ddt will try to convert the passed data into python identifiers for display as data. For example, (3,2) is transformed into 3_ 2. It should be noted that if the data is a dictionary, this is the key of the dictionary.

Use example

1. Put data directly into the value

You need to import the ddt package, then decorate the TestCase class with @ ddt, and decorate the test method with @ data().

data can be numeric or string.

import unittest
from ddt import ddt, data
from ddt_demo.mycode import larger_than_two

@ddt
class FooTestCase(unittest.TestCase):
    
    @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(u'ascii', u'non-ascii-\N{SNOWMAN}')
    def test_unicode(self, value):
        self.assertIn(value, (u'ascii', u'non-ascii-\N{SNOWMAN}'))
        
if __name__=='__main__':
    unittest.main(verbosity=2)

The output is as follows:

test_larger_than_two_1_3 (__main__.FooTestCase) ... ok
test_larger_than_two_2_4 (__main__.FooTestCase) ... ok
test_larger_than_two_3_12 (__main__.FooTestCase) ... ok
test_larger_than_two_4_23 (__main__.FooTestCase) ... ok
test_not_larger_than_two_1_1 (__main__.FooTestCase) ... ok
test_not_larger_than_two_2__3 (__main__.FooTestCase) ... ok
test_not_larger_than_two_3_2 (__main__.FooTestCase) ... ok
test_not_larger_than_two_4_0 (__main__.FooTestCase) ... ok
test_unicode_1_ascii (__main__.FooTestCase) ... ok
test_unicode_2_non_ascii__ (__main__.FooTestCase) ... ok

----------------------------------------------------------------------
Ran 10 tests in 0.001s

OK

You can see that only 3 test methods are written above, but 10 use cases are run in the end.

Here, a suffix will be added to the test method by ddt. ddt will try to convert the test data into a suffix attached to the test method to form a new name.

2. Put data into complex data structure

When using complex data structures, @ unpack is required, and the parameters of the test method need to use multiple corresponding ones, such as frist below_ Value and second_value.

import unittest
from ddt import ddt, data,unpack

@ddt
class FooTestCase(unittest.TestCase):

    @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)
        
if __name__=='__main__':
    unittest.main(verbosity=2)

After execution, all pass.

3. Use json file

New file test_data_list.json:

[
    "Hello",
    "Goodbye"
]

new file   test_data_dict.json:

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

New test script ddt_test.py:

import unittest
from ddt import ddt, file_data
from ddt_demo.mycode import has_three_elements,is_a_greeting

@ddt
class FooTestCase(unittest.TestCase):

    @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))
        
if __name__=='__main__':
    unittest.main(verbosity=2)

4. Use yaml file

New file test_data_list.yaml:

- "Hello"
- "Goodbye"

New file test_data_dict.yaml:

unsorted_list:
  - 10
  - 15
  - 12

sorted_list: [ 15, 12, 50 ]

New test script ddt_test.py:

import unittest
from ddt import ddt, file_data
from ddt_demo.mycode import has_three_elements,is_a_greeting

@ddt
class FooTestCase(unittest.TestCase):

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

    @file_data('test_data_list.yaml')
    def test_file_data_yaml_list(self, value):
        self.assertTrue(is_a_greeting(value))
        
if __name__=='__main__':
    unittest.main(verbosity=2)

Posted on Sat, 27 Nov 2021 18:21:41 -0500 by vincent30000