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

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

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

iTesting,爱测试,爱分享

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

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

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

代码语言:javascript
复制
# 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:

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

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

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

再看它对应的测试类

代码语言:javascript
复制
# 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定义如下:

代码语言: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

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

代码语言:javascript
复制
#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

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

代码语言:javascript
复制
#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的测试方法放入我们的测试用例里。

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

代码语言:javascript
复制
#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)

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

运行结果如下:

代码语言:javascript
复制
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记录

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

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

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

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

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

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