装饰器重写ddt方法完成数据驱动测试
from unittest import TestCase
def decorator(cls):
attrs = list(cls.__dict__.items())
for name, value in attrs:
if hasattr(value, 'PARAM_DATA'):
cases = getattr(value, 'PARAM_DATA')
for index, case in enumerate(cases, start=1):
# 1.构造测试方法名称
test_method_name = f'{name}_{index}'
# 2.获取测试方法的执行逻辑(测试方法引用)
test_method = value
# 3.动态设置测试方法
setattr(cls, test_method_name, test_method)
delattr(cls, name)
return cls
def TestData(data_list):
"""
给测试方法绑定测试数据
:param data_list:
:return:
"""
def wrapper(func):
setattr(func, 'PARAM_DATA', data_list)
return func
return wrapper
@decorator
class TestLogin(TestCase): # TestLogin = decorator(TestLogin)
"""
数据驱动测试
"""
cases1 = [
{'title': "用例1", "data": 1111},
{'title': "用例2", "data": 2222},
{'title': "用例3", "data": 3333},
{'title': "用例4", "data": 4444}
]
cases2 = [
{'title': "用例11", "data": 1111},
{'title': "用例22", "data": 2222},
{'title': "用例33", "data": 3333},
{'title': "用例44", "data": 4444}
]
@TestData(cases1) # TestData(cases1) -> wrapper -> @wrapper ->test_login = wrapper(test_login)
def test_login(self):
"""
测试登录接口
:return:
"""
print("输入用户名")
print("输入密码")
self.assertTrue(1 == 1)
@TestData(cases2)
def test_register(self):
"""
注册接口测试
:return:
"""
pass
这段代码是一个 Python 的单元测试相关的程序。它主要用于构建数据驱动的测试用例,使得测试用例可以采用参数化的方式重复执行,实现更全面的测试。
首先,我们定义了一个名为
decorator
的装饰器函数。它接收一个类作为参数,并对类中所有标记了TestData
装饰器的方法进行处理。具体而言,它会将这些方法的参数列表PARAM_DATA
展开后进行遍历,对每个参数组合构造一个新的测试方法,并将其添加到原类中的方法列表中。最后,将原有的参数化方法删除。然后,我们定义了一个
TestData
装饰器函数。它接收一个参数列表data_list
,并返回一个闭包wrapper
。该闭包函数会将接收到的参数列表保存在测试方法的内部属性PARAM_DATA
中,并将其返回。在被装饰的方法上,TestData
函数的调用会被自动展开为一个闭包函数的调用,并将data_list
作为参数传入,从而为测试方法动态添加属性PARAM_DATA
。最后,我们定义了一个名为
TestLogin
的测试类,并在其中定义了两个被TestData
装饰器修饰的测试方法test_login()
和test_register()
。每个方法都包含一个名为casesN
(N 可以是任意数字)的测试数据列表,用于保存测试方法的参数组合。这些测试数据列表会在decorator
装饰器函数中被处理,并根据参数组合创建新的测试方法。
我们对以上代码进行断点分析
attrs:
[('__module__', 'test_03_装饰器执行数据驱动测试优化1'), ('__doc__', '\n 数据驱动测试\n '), ('cases1', [{'title': '用例1', 'data': 1111}, {'title': '用例2', 'data': 2222}, {'title': '用例3', 'data': 3333}, {'title': '用例4', 'data': 4444}]), ('cases2', [{'title': '用例11', 'data': 1111}, {'title': '用例22', 'data': 2222}, {'title': '用例33', 'data': 3333}, {'title': '用例44', 'data': 4444}]), ('test_login', <function TestLogin.test_login at 0x00000242694F1DC0>), ('test_register', <function TestLogin.test_register at 0x00000242694F1EE0>)]
-----------------------------
# 满足条件的name和value
name
'test_login'
-----------------------------
value
<function TestLogin.test_login at 0x000001B3DB641820>
-----------------------------
cases
[{'data': 1111, 'title': '用例1'}, {'data': 2222, 'title': '用例2'}, {'data': 3333, 'title': '用例3'}, {'data': 4444, 'title': '用例4'}]
-----------------------------
test_method_name
'test_login_1'
这段代码实现了数据驱动测试,在 TestCase
类的基础上,通过装饰器 decorator
和装饰器函数 TestData
来为测试方法动态绑定测试数据。
首先,decorator
装饰器函数定义如下:
def decorator(cls):
"""
用于构造测试方法
"""
attrs = list(cls.__dict__.items())
for name, value in attrs:
if hasattr(value, 'PARAM_DATA'):
cases = getattr(value, 'PARAM_DATA')
for index, case in enumerate(cases, start=1):
# 1.构造测试方法名称
test_method_name = f'{name}_{index}'
# 2.获取测试方法的执行逻辑(测试方法引用)
test_method = value
# 3.动态设置测试方法
setattr(cls, test_method_name, test_method)
delattr(cls, name)
return cls
其作用是遍历 cls
类中的所有属性,如果某个属性具有 PARAM_DATA
属性,则将其对应的测试数据拆分出来,分别构造新的测试方法,并动态绑定到原有的测试类中。最后,删除原有的测试方法。
接下来,TestData
装饰器函数定义如下:
def TestData(data_list):
"""
给测试方法绑定测试数据
:param data_list: 测试数据列表
:return: 装饰器函数
"""
def wrapper(func):
func.PARAM_DATA = data_list # 为测试方法动态添加属性
return func
return wrapper
其作用是为测试方法动态绑定测试数据,具体实现方式是使用闭包,将 data_list
传入到 wrapper
函数中,并将其绑定到测试方法的 PARAM_DATA
属性上。
最后,我们通过在测试类中使用 @TestData
装饰器来动态绑定测试数据,如下所示:
@decorator
class TestLogin(TestCase):
cases1 = [
{'title': "用例1", "data": 1111},
{'title': "用例2", "data": 2222},
{'title': "用例3", "data": 3333},
{'title': "用例4", "data": 4444}
]
cases2 = [
{'title': "用例11", "data": 1111},
{'title': "用例22", "data": 2222},
{'title': "用例33", "data": 3333},
{'title': "用例44", "data": 4444}
]
@TestData(cases1)
def test_login(self):
"""
测试登录接口
:return:
"""
print("输入用户名")
print("输入密码")
self.assertTrue(1 == 1)
@TestData(cases2)
def test_register(self):
"""
注册接口测试
:return:
"""
pass
其中,cases1
和 cases2
分别是测试方法 test_login
和 test_register
对应的测试数据列表。通过在测试方法上方使用 @TestData
装饰器,并传入对应的测试数据列表,来动态绑定测试数据。
最后,通过运行测试类,可以自动生成多组测试用例,每个测试用例都有对应的测试数据。这样就可以大大简化测试代码,提高测试效率。
评论