修改ddt 生成的测试用例名称,首先得了解ddt 生成的测试用例名称的逻辑
ddt生成的测试用例名称的逻辑
以该篇文章进行举例:DDT+Excel进行接口测试
通过ddt 验证多组数据测试场景时,生成的测试报告,会根据测试用例的名称test_verify_phone ,自动在测试用例名称后加上数字,如test_verify_phone_1 、test_verify_phone_2 … 这些数字与测试数据的一一对应
测试用例py文件
@ddt.ddt
class Test_Verify_Code(unittest.TestCase):
def setUp(self) -> None:
pass
def tearDown(self) -> None:
pass
@ddt.data(*test_data)
def test_verify_phone(self,test_data):
res = HTTPHandler().visit(test_data["url"],test_data["method"],json=test_data["phonenum"])
self.assertEqual(res["code"],test_data["excepted"])
将鼠标光标移至 @ddt.data(*test_data) 的 data 处,按住Ctrl,点击 data 处,查看源码内容
获取测试数据的数量
源码文件
def data(*values):
"""
Method decorator to add to your test methods.
Should be added to methods of instances of ``unittest.TestCase``.
"""
global index_len
index_len = len(str(len(values)))
return idata(values)
有一个 data函数 ,通过获取测试数据的长度(数量),获取测试数据的数量。 在 index_len = len(str(len(values))) 这一行打上断点,然后通过DEBUG 的方式,执行测试用例py文件 这里的values ,其实就是测试数据,这是一个tuple 元组类型的数据,里面有4条dict 字典类型的数据 当然,起作用的,主要是下面的一个idata 函数 这个函数的作用,是生成测试用例的个数,通过ddt 验证了多少组数据,就会有多少个测试用例
这是一个返回函数,这个函数就比较有意思了,内容很丰富,具体的逻辑,不深究了,如果有兴趣,可以在关键步骤继续打断点,一步步调试,查看数据走向
生成测试用例名称
源码文件
def mk_test_name(name, value, index=0, name_fmt=TestNameFormat.DEFAULT):
index = "{0:0{1}}".format(index + 1, index_len)
if name_fmt is TestNameFormat.INDEX_ONLY or not is_trivial(value):
return "{0}_{1}".format(name, index)
try:
value = str(value)
except UnicodeEncodeError:
value = value.encode('ascii', 'backslashreplace')
test_name = "{0}_{1}_{2}".format(name, index, value)
return re.sub(r'\W|^(?=\d)', '_', test_name)
有一个 mk_test_name函数 ,该函数的作用是:通过ddt 验证多组数据测试场景时生成对应的测试用例名称。 index=0 ,最开始的时候为0;index + 1 ,进行 +1 处理;
"test_name ={0}_{1}".format(name, index) ,这行明确说明了返回的测试用例名称的格式。如果有兴趣,也可以在这行打一个断点,DEBUG 调试一下
ddt生成的测试用例名称
回到测试用例文件,将鼠标光标移至 @ddt.ddt 处,按住Ctrl,点击第二个 ddt 处,查看源码内容 源码文件
def ddt(arg=None, **kwargs):
fmt_test_name = kwargs.get("testNameFormat", TestNameFormat.DEFAULT)
def wrapper(cls):
for name, func in list(cls.__dict__.items()):
if hasattr(func, DATA_ATTR):
for i, v in enumerate(getattr(func, DATA_ATTR)):
test_name = mk_test_name(name,getattr(v, "__name__", v),i,fmt_test_name)
test_data_docstring = _get_test_data_docstring(func, v)
if hasattr(func, UNPACK_ATTR):
if isinstance(v, tuple) or isinstance(v, list):
add_test(cls,test_name,test_data_docstring,func,*v)
else:
add_test(cls,test_name,test_data_docstring,func,**v)
else:
add_test(cls, test_name, test_data_docstring, func, v)
delattr(cls, name)
elif hasattr(func, FILE_ATTR):
file_attr = getattr(func, FILE_ATTR)
process_file_data(cls, name, func, file_attr)
delattr(cls, name)
return cls
这块就是最关键的部分了 在这两行打断点,一步步进行调试,可以发现 name 在这里,其实就是测试用例的名称test_verify_phone ,test_name 这里调用了mk_test_name 函数,所以,在 test_verify_phone 后加上了数字,这时候 test_name = test_verify_phone_1
ddt 生成的测试用例名称的逻辑,就是如此了。所以通过ddt 验证的测试场景,在测试用例名称后都会加上对应的数字。
修改ddt生成的测试用例名称
然而,这种情况,有些时候,并不知道这个测试用例对应的是哪个测试场景。
假设这时候,领导说看日志很麻烦,要你把这个测试报告里的内容描述清楚,他想知道哪个场景是通过的,哪个是没有通过的,让你把测试用例的标题改一下,这一个测试用例是测什么场景的,要让他能一眼就能看懂。 虽然心里把他骂了几十遍,但最终你还是得按他的要求完成
在上面的调试过程中,可以发现 v 这个变量,指的是每一组测试数据的内容,所以,只需要在每组测试数据里,加上描述的内容,然后再拼接到生成的测试用例名称上就行了
从excel读取的数据,一般以字典或列表的形式进行存储。 判断一下,如果测试数据是 list 列表类型,则取这个列表里的第一个元素的内容;如果测试数据是 dict 字典类型,则取这个字典里case_name 的值
所以,修改ddt 生成的测试用例名称,并不难 在 test_name = mk_test_name(name,getattr(v, "__name__", v),i,fmt_test_name) 后面加上几行代码就行了
if isinstance(v, list):
test_name = mk_test_name(name, v[0], i)
elif isinstance(v, dict):
test_name = mk_test_name(name, v['case_name'], i)
当然,我们还可以再严谨一点,如果读取的数据,都不是字典、列表的类型,这时候抛出异常,但又不影响原来ddt 的功能,可以这样修改:
def ddt(arg=None, **kwargs):
fmt_test_name = kwargs.get("testNameFormat", TestNameFormat.DEFAULT)
def wrapper(cls):
for name, func in list(cls.__dict__.items()):
if hasattr(func, DATA_ATTR):
for i, v in enumerate(getattr(func, DATA_ATTR)):
try:
if isinstance(v, list):
test_name = mk_test_name(name, v[0], i)
elif isinstance(v, dict):
test_name = mk_test_name(name, v['case_name'], i)
except Exception as e:
test_name = mk_test_name(name,getattr(v, "__name__", v),i,fmt_test_name)
logging.exception(e)
test_data_docstring = _get_test_data_docstring(func, v)
if hasattr(func, UNPACK_ATTR):
if isinstance(v, tuple) or isinstance(v, list):
add_test(cls,test_name,test_data_docstring,func,*v)
else:
add_test(cls,test_name,test_data_docstring,func,**v)
else:
add_test(cls, test_name, test_data_docstring, func, v)
delattr(cls, name)
elif hasattr(func, FILE_ATTR):
file_attr = getattr(func, FILE_ATTR)
process_file_data(cls, name, func, file_attr)
delattr(cls, name)
return cls
不推荐在 ddt 这个py文件里直接改源码,如果这样直接修改了,会造成 ddt 只符合这一个测试用例的场景,如果其他的测试用例数据内容不是这样的,可能就会产生报错
找到ddt 这个py文件所在的目录(一般都在 python环境变量 下的 site-packages 目录下),将其复制一份,放到项目路径下的 Common 目录下,然后对Common 目录下ddt.py 文件进行修改
在测试用例py文件下,修改ddt 的导入方式 :from Common import ddt 这样,使用的ddt 功能,就是符合个测试用例场景的功能了
生成报告
|