Python 資料驅動工具:DDT

微微微笑發表於2017-11-20

背景

python 的unittest 沒有自帶資料驅動功能。

所以如果使用unittest,同時又想使用資料驅動,那麼就可以使用DDT來完成。

DDT是 “Data-Driven Tests”的縮寫。

資料:http://ddt.readthedocs.io/en/latest/

使用方法

dd.ddt:

裝飾類,也就是繼承自TestCase的類。

ddt.data:

裝飾測試方法。引數是一系列的值。

ddt.file_data:

裝飾測試方法。引數是檔名。檔案可以是json 或者 yaml型別。

注意,如果檔案以”.yml”或者”.yaml”結尾,ddt會作為yaml型別處理,其他所有檔案都會作為json檔案處理。

如果檔案中是列表,每個列表的值會作為測試用例引數,同時作為測試用例方法名字尾顯示。

如果檔案中是字典,字典的key會作為測試用例方法的字尾顯示,字典的值會作為測試用例引數。

ddt.unpack:

傳遞的是複雜的資料結構時使用。比如使用元組或者列表,新增unpack之後,ddt會自動把元組或者列表對應到多個引數上。字典也可以這樣處理。參見下面的示例2.

測試用例方法名生成規則

使用ddt後,會產生一個新的測試用例方法名:之前的測試用例方法名_ordinal_data

之前的測試用例方法名:即定義的測試用例方法名。比如def test_large(),這裡就是test_large

ordinal:整數,從1開始遞加。

data:如果傳遞過來的資料存在__name__屬性,則這裡就是該資料的__name__值。如果未定義__name__屬性,ddt會盡量將傳遞過來的資料轉化為python識別符號,作為data顯示。比如(3,2)就轉化為3_2。需要注意的是,如果資料是字典,則這裡就是字典的key。

使用示例

1. data直接放入數值

需要匯入ddt包,然後再TestCase類上採用@ddt進行裝飾,測試方法上裝飾@data()。

data可以是數值,也可以是字串。

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)

輸出如下:

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

可以看到上面只寫了3個測試方法,但是最後run了10個用例。

這裡測試方法後會被ddt加一個字尾,ddt會嘗試把測試資料轉化為字尾附在測試方法後,組成一個新的名字。

2. data放入複雜的資料結構

使用複雜的資料結構時,需要用到@unpack,同時測試方法的引數需要使用對應的多個,比如下面的frist_value 以及 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)

執行之後,全部pass。

3. 使用json檔案

新建檔案 test_data_list.json:

[
    "Hello",
    "Goodbye"
]

新建檔案  test_data_dict.json:

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

新建測試指令碼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. 使用yaml檔案

新建檔案 test_data_list.yaml:

- "Hello"
- "Goodbye"

新建檔案 test_data_dict.yaml:

unsorted_list:
  - 10
  - 15
  - 12

sorted_list: [ 15, 12, 50 ]

新建測試指令碼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)

相關文章