Python数据驱动实践(四)–动态挑选测试用例

iTesting,爱测试,爱分享

前面几天, 我从数据驱动的一个第3方库ddt出发,连续分享了3篇文章: Python数据驱动实践(一)–ddt实现数据驱动 Python数据驱动实践(二)–教你用Python实现数据驱动 Python数据驱动实践(三)–动态添加测试用例

后面两篇文章实际上是任何一个测试框架都必须要有的部分。 今天我再分享一篇如何动态挑选测试用例, 大家知道,自动化脚本越写越多,但不是每次都需要full regression, 这个时候需要把开发修改涉及到的测试用例跑一下,而那些无关的用例可以不跑。

那么,思路是什么? 如果对每一个用例,我定义的时候给一个标签比如说Test,再给它一个值,True或False,这样我框架寻找测试用例的时候就找标签编辑为Test且值是True的就好了。 老规矩,先上代码:

# test_decorator.py
def TestClass(enabled=True):
    def wraps(cls):
        setattr(cls, "__test_type__", "TestClass")
        setattr(cls, "__enabled__", enabled)
        return cls
    return wraps
def Test(enabled= True):
    def wraps(func):
        setattr(func, "__test_type__", "Test")
        setattr(func, "__enabled__", enabled)
        return func
    return wraps
# 利用装饰器,对于每一个进来的类或func,
#我们给它加上一个属性一个值,然后再你需要运行的类或者func上装饰就好了。

结合我们上次讲过的动态添加测试用例, 和数据驱动,我们把这部分整合起来看看,一个简单完整的测试框架如下。 1.从指定的文件夹/文件下查找待运行测试类/方法 2.找到待运行测试类/方法,并根据数据不同重新生成测试用例 3.运行测试用例集并保存运行结果

我的整个项目层次结构是这样的:

其中: Common:

放通用的功能,比如,查找待测试用例的test_case_finder, 和我们上文的定义是否测试类/测试函数的装饰器test_decorator

pages:

放我们所有的页面功能,如果是UI测试,这个就对应于一个个的UI Page,我们将以Page Object组织页面元素,并定义页面类和方法,每一个页面当作一个page看待并定义一个类

tests:

这个下面放我们的测试用例,结构跟pages完全对应,每一个测试类对应于一个page 类。

run:

这个文件定义了用例如何运行,是并行还是顺序。

还有其它的很多功能,我们暂且不讲,先来看看这几个如何协作的。

# pages/page_add.py
#我们就定义个多数相加的方法。
class SumData:
    def __init__(self):
        pass
    def sum_data(self, *ags):
        return sum(ags)

再看它对应的测试类

# tests/test_page1/test_page_add.py
#这里面我定义了两个测试方法,一个要参数,一个不要。
#并且用了两个装饰类,一个用来提供测试数据,一个用来标记是否需要测试
from common.test_case_finder import data_provider
from common.test_decorator import Test
from pages.page_add import SumData
class TestSumData:
    def __init__(self):
        pass
    @data_provider([(1, 2, 3), (4, 5, 9)])
    @Test()
    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
    @Test(enabled=False)
    def test_sum_data2(self):
        print(2)
        assert SumData().sum_data(4, 5) == 7

这两个装饰类的定义Test()我们前面已经介绍过了,test provider定义如下:

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

好了,有了page类,有个test类,那么如何让我们的程序查找到我们需要运行的测试用例呢?

#test_case_finder.py
# 简化下,只给出如何鉴别待运行的用例
#mod_ref就是我们动态加载进来的所有测试module。
def find_classes_in_module(self, mod_ref):
    test_classes = []
    for module in mod_ref:
    #微信关注公众号iTesting,加入万人测试团
        cls_members = inspect.getmembers(module, inspect.isclass)
        for cls in map(lambda x: x[1], cls_members):
            for name, func in list(cls.__dict__.items()):
                if hasattr(func, "__test_type__") and hasattr(func, "__enabled__"):
                    if getattr(func, "__test_type__") == "Test" and getattr(func, "__enabled__") == True:
                        test_classes.append(func)
    return test_classes

那么,用例找出来了,如何数据驱动呢?

#test_case_finder.py
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 unpack_test_cases_from_functions(func_list):
    return_cases = []
    for func in func_list:
        if hasattr(func,  '__data_Provider__'):
            for i, v in enumerate(getattr(func, "__data_Provider__")):
                test_name = mk_test_name(func.__name__, getattr(v, "__name__", v), i)
                return_cases.append((test_name, func, v))
        else:
            return_cases.append((func.__name__, func, None))
    return return_cases
#我们通过反射的方式找到有Test()装饰的测试方法,并且只把enabled=True的测试方法放入我们的测试用例里。

测试用例也找到了,该运行了:

#run.py
#简化版, 实现了一个简单的顺序执行,我们后面将一步步优化至并发,顺序都可以
import traceback
from common.test_case_finder import DiscoverTestCases, unpack_test_cases_from_functions
if __name__ == "__main__":
    #下面一句给定了查找测试用例的根目录,如果不给定,我们默认从tests文件夹找
    mypath = r"D:\ktest\tests\test_page1" 
    cases_to_run = []
    cases_run_success = []
    cases_run_fail = []
    discover_cases = DiscoverTestCases(mypath)
    mds = discover_cases.get_modules_spec()
    cases_to_run = unpack_test_cases_from_functions(discover_cases.find_classes_in_module(mds))
    while cases_to_run:
        try:
            name, func, value = cases_to_run.pop(0)
            if value:
                func.__call__(name, *value)
            else:
                func.__call__(name)
        except:
            # traceback.print_exc()
            cases_run_fail.append(name)
        else:
            cases_run_success.append(name)
    print('Below cases are passed:\n %s' %cases_run_success)
    print('Below cases are failed:\n %s' % cases_run_fail)

#以上给定了一个简单的方法,找到所有的呆测试用例,
#然后一个个执行,并且将测试成功和失败用例集分别存放,方便后续测试报告的生成。

运行结果如下:

Below cases are passed:
 ['test_sum_data_1__1__2__3_', 'test_sum_data_2__4__5__9_']
Below cases are failed:
 []

基本上实现了我们的需求,但是一个测试框架,要考虑的还很多,比如: 1.测试fixture,包括运行前准备和运行后清理。 2.并发执行 3.测试报告 4.邮件发送 5.错误截图 6.log记录

我后续将带你逐个击破,最终写出自己的测试框架,敬请期待。

本文分享自微信公众号 - iTesting(TesterTalk)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-10-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏AI科技大本营的专栏

伯克利人工智能研究院开源深度学习数据压缩方法Bit-Swap,性能创新高

该研究的目标是,设计一种可用于图像等高维数据的高效无损压缩方法。实现这一目标,要同时解决两个问题:

8900
来自专栏AI科技大本营的专栏

18段代码带你玩转18个机器学习必备交互工具

作者 | 曼纽尔·阿米纳特吉(Manuel Amunategui)、迈赫迪·洛佩伊(Mehdi Roopaei)

13800
来自专栏AI科技大本营的专栏

24式加速你的Python

9500
来自专栏汇智网教程

plotly可视化快速教程

Plotly是新一代的Python数据可视化开发库,它提供了完善的交互能力和灵活的绘制选项。本文将介绍新手如何安装plotly并编写第一个plotly绘图程序,...

30100
来自专栏小小挖掘机

线性代数在数据科学中的十个强大应用(一)

本篇主要介绍了机器学习与数据科学背后的数学技术十大应用之基础机器学习部分与降维部分。

11600
来自专栏小詹同学

27 个问题,告诉你Python为什么这么设计

看到豌豆花下猫在 Python 猫公众号推的这篇文章,虽说是从文档里节选的,但是对深入学习Python很有价值,也推荐给大家。

9800
来自专栏汇智网教程

机器学习UI开发框架Streamlit快速教程

Streamlit是第一个专门针对机器学习和数据科学团队的应用开发框架,它是开发自定义机器学习工具的最快的方法,你可以认为它的目标是取代Flask在机器学习项目...

29900
来自专栏AI科技大本营的专栏

图解Python算法

背包问题有很多种解决办法,每一种都对应一种算法。把这个问题想清楚了,你至少可以成为半个算法高手。

9300
来自专栏小詹同学

从Zero到Hero,一文掌握Python关键代码

首先,什么是 Python?根据 Python 创建者 Guido van Rossum 所言,Python 是一种高级编程语言,其设计的核心理念是代码的易读性...

9900
来自专栏AI科技大本营的专栏

如果不懂Numpy,请别说自己是Python程序员

大约七八年前,我曾经用 pyOpenGL 画过地球磁层顶的三维模型,这段代码至今仍然还运行在某科研机构里。在那之前,我一直觉得自己是一个合(you)格(xiu)...

9100

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励