python unittest ddt(數據驅動測試)插件源代碼分析

unittest需要第三方插件ddt來實現數據驅動。數據可以是列表、字典或者JSON文件。

>pip install ddt進行安裝

下面是一個簡單的使用舉例。

import unittest

import ddt

@ddt.ddt

class TestAdd(unittest.TestCase):

    @ddt.data([1,2,3],[3,4,7])

    @ddt.unpack

    def test_add(self,a,b,c):

        assert a+b==c

則分別使用提供的兩組數據,執行兩個test。

提供多少組數據,就創建多少個測試方法。這裏創建的測試方法是test_add_1__1__2__3_ 和

test_add_2__3__4__7_ (__main__.TestAdd)。

@ddt.file_data('data.json')實現讀取json數據,內容可以是列表或者字典。

 

excel數據驅動開發思路:

將讀取出的excle文件中的數據以雙重列表的方式返回,作爲@ddt.data()的參數。

比如:實現一個函數,get_data_from_sheet(excel_file_path,sheet_name)。以雙重列表的方式返回data。@ddt.data(*get_data_from_sheet(excel_file_path,sheet_name))使用數據。

 

ddt源代碼分析:

下面以處理列表數據爲例來分析,處理字典數據和json數據文件的主要流程是相同的。

ddt(arg=None, **kwargs)中對於每一組數據,執行test_name = mk_test_name(name,getattr(v, "__name__", v),i,fmt_test_name)構造新test函數的名字,其中name是原test函數的名字,v是一組數據,i是這組數據的index,fmt_test_name指定新的test函數的名字的格式。

然後執行add_test(cls,test_name,test_data_docstring,func,*v)基於原test函數func生成將新的test函數test_name,加入原testcase類cls,test_data_docstring 是新的test函數的docstring,v是對應的那組數據。

最後執行delattr(cls, name),刪除原test函數。原test函數已經通過各組數據具體化成了新的test函數,所以已經不需要它了。留着它,執行時會拋出異常,TypeError: test_add() missing 3 required positional arguments: 'a', 'b', and 'c'。

 

下面來重點看一下生成新test函數的邏輯。

add_test(cls, test_name, test_docstring, func, *args, **kwargs)中setattr(cls, test_name, feed_data(func, test_name, test_docstring, *args, **kwargs))。

feed_data(func, new_name, test_data_docstring, *args, **kwargs)中

    @wraps(func)

    def wrapper(self):

        return func(self, *args, **kwargs)

    wrapper.__name__ = new_name

    wrapper.__wrapped__ = func

    wrapper.__doc__ = test_data_docstring

    return wrapper

在原來的test函數func的基礎上使用具體參數值,生成新的test函數,名字是new_name。然後將這個函數作爲一個方法添加到原測試用例類cls中。

爲了方便大家理解,下面我把生成新test函數的邏輯提取出來,寫出下面的簡化實現,以及對其使用的舉例。

#下面兩個函數生成新的test函數

def add_test(cls, test_name, func, *args):

    setattr(cls, test_name, feed_data(func, test_name, *args))

def feed_data(func, new_name, *args):

    def wrapper(self):

        return func(self, *args)

    wrapper.__name__ = new_name

    return wrapper

#下面是使用舉例

import unittest

class TestAdd(unittest.TestCase):

    def test_add(self,a,b,c):

        self.assertEqual(a+b,c)

add_test(TestAdd,"test_add_1_1_2_3",TestAdd.test_add,1,2,3)

add_test(TestAdd,"test_add_1_3_4_7",TestAdd.test_add,3,4,7)

delattr(TestAdd,"test_add")

unittest.main(verbosity=2)

'''

運行結果:

Total number of cases:2

test_add_1_1_2_3 (__main__.TestAdd) 1 ... ok

test_add_1_3_4_7 (__main__.TestAdd) 2 ... ok

----------------------------------------------------------------------

Ran 2 tests in 0.000s

OK

'''

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章