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

'''

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