前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python数据驱动实践(三)–动态添加测试用例

Python数据驱动实践(三)–动态添加测试用例

作者头像
iTesting
发布2019-10-29 16:34:45
4210
发布2019-10-29 16:34:45
举报
文章被收录于专栏:iTestingiTesting

iTesting,爱测试,爱分享

前面我们讲了什么是python数据驱动,如何使用及如何实现数据驱动。没看过文章的请移步: Python数据驱动实践(一)–ddt实现数据驱动 Python数据驱动实践(二)–教你用Python实现数据驱动

今天我们来解决另外一个问题,实现数据驱动后,如何在测试报告里体现?

老规矩,先上段代码:

代码语言:javascript
复制
# 这段代码实现了一个事情, 测试一个sum_data的方法。
from functools import wraps
def data_provider(test_data):
    def wrapper(func):
        @wraps(func)
        def repl(self, *args, **kwargs):
            for i in test_data:
                print('func starts')
                func(self, *i, **kwargs)
                print('func ends')
        return repl
    return wrapper
class SumData:
    def __init__(self):
        pass
    def sum_data(self, *ags):
        return sum(ags)
class TestSumData:
    def __init__(self):
        pass
    @data_provider([(1, 2, 3), (4, 5, 9)])
    def test_sum_data(self, x, y, z):
        print("the value are {0}, {1}, {2}".format(x, y, z))
        assert SumData().sum_data(x, y) == z
if __name__ == "__main__":
    print(TestSumData().test_sum_data())
#唯一需要注意的是,@wraps的作用:
#由于装饰器的加入导致Python解释器认为函数本身发生了改变,
#所以用@wraps,
#它可以将原函数对象的指定属性复制给包装函数对象,
#保证装饰器不会对被装饰函数造成影响

我们知道,运行测试用例时候,通常会给一个待运行函数的集合,针对集合的每一个函数,逐个运行一遍,然后收集每个测试函数(用例)的名字,运行结果,然后保存,待生成测试报告用。

我们当前的代码, 根据data provider提供的数据不同,test_sum_data 这个函数也会运行多次,但是测试报告里只有一个test_sum_data, 那么如何解决呢? 1.先检查要运行的测试函数有没有data provider。 2.对于每个有data provider的测试函数,针对每一条数据,生成一个新的名字。 3.把新的名字加入到要运行的test case列表里。

根据这个我们知道,我们代码少两个函数实现, 一个是检查函数有没有data provider, 二是给有data provider装饰的函数生成新名字。怎么实现,当然是装饰器啦。 那么如何检查函数有没有提供data呢?我们可以人为给提供了data的函数加些属性。

代码语言:javascript
复制
def data_provider(test_data):
    def wrapper(func):
        setattr(func, "__data_Provider__", test_data)
        global index_len
        index_len = len(str(len(test_data)))
        return func
    return wrapper

如何给有__data__provider __ 属性的函数生成新名字呢?

代码语言:javascript
复制
def mk_test_name(name, value, index=0):
    index = "{0:0{1}}".format(index+1, index_len)
    test_name = "{0}_{1}_{2}".format(name, index, value)
    return re.sub(r'\W|^(?=\d)', '_', test_name)

那么,怎么组织把这些命名的新函数添加到待运行列表呢?

代码语言:javascript
复制
def get_test_cases(cls):
    return_cases = []
    for name, func in list(cls.__dict__.items()):
        if hasattr(func,  '__data_Provider__'):
            for i, v in enumerate(getattr(func, "__data_Provider__")):
                test_name = mk_test_name(name, getattr(v, "__name__", v), i)
                return_cases.append((test_name, func, v))
    return return_cases

最后我们整理下,看看实际上是如何使用的。

代码语言:javascript
复制
import re
def data_provider(test_data):
    def wrapper(func):
        setattr(func, "__data_Provider__", test_data)
        global index_len
        index_len = len(str(len(test_data)))
        return func
    return wrapper
class SumData:
    def __init__(self):
        pass
    def sum_data(self, *ags):
        return sum(ags)
class TestSumData:
    def __init__(self):
        pass
    @data_provider([(1, 2, 3), (4, 5, 6)])
    def test_sum_data(self, x, y, z):
        print("the value are {0}, {1}, {2}".format(x, y, z))
        assert SumData().sum_data(x, y) == z
def mk_test_name(name, value, index=0):
    index = "{0:0{1}}".format(index+1, index_len)
    test_name = "{0}_{1}_{2}".format(name, index, value)
    return re.sub(r'\W|^(?=\d)', '_', test_name)
def get_test_cases(cls):
    return_cases = []
    for name, func in list(cls.__dict__.items()):
        if hasattr(func,  '__data_Provider__'):
            for i, v in enumerate(getattr(func, "__data_Provider__")):
                test_name = mk_test_name(name, getattr(v, "__name__", v), i)
                return_cases.append((test_name, func, v))
    return return_cases
if __name__ == "__main__":
    cases_to_run = []
    cases_run_success = []
    cases_run_fail = []
    #注1
    cases_to_run = get_test_cases(TestSumData)
    while cases_to_run:
        try:
            case = cases_to_run.pop(0)
            name = case[0]
            func = case[1]
            v = case[2]
            func(name, *v)
        except:
            # traceback.print_exc()
            cases_run_fail.append(name)
        else:
            cases_run_success.append(name)
    print(cases_run_success)
    print(cases_run_fail)
#output
the value are 1, 2, 3
the value are 4, 5, 6
['test_sum_data_1__1__2__3_']
['test_sum_data_2__4__5__6_']

我们定义并维护了三个列表,一个是cases_to_run,放我们找到的所有的测试用例,一个是cases_run_success,放运行成功的用例,最后一个 cases_run_fail, 放测试不成功的用例。

从运行结果我们可以看出, 我们只有一个测试函数test_sum_data,测试报告里却有了两个用例test_sum_data112__3, test_sum_data245__6。

就这样我们就实现了测试报告里数据驱动可视化。

Hold on, 看我上面代码的注释(注1),我们传入的是固定的测试类,那么在真实的测试中,我希望框架去自动查找测试类和测试方法,这个怎么搞?

请期待下次分享 :)

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-09-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 iTesting 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档