前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python数据驱动深入实践(二)

Python数据驱动深入实践(二)

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

在做自动化测试时,我们通常有这样的需求,对某一个用例,希望可以根据我们提供的不同数据而运行多次,每次运行加载一条数据,但是运行的是同一个用例,仅仅是数据不同。 这个模式Java里TestNG有个概念叫DataProvider, python里也有,叫ddt。 上次我们介绍了用法,连接在此 -- Python数据驱动深入实践(一) ,今天来讲下如何自己实现一个ddt。

我们先来看一个简单例子:

def sum_data(x, y):
    return x + y


if __name__ == "__main__":
    assert sum_data(1, 2) == 3
    assert sum_data(4, 5) ==8

正常来说, 要验证这个函数的实现正确与否,最基本的两个检查点一定是一个通过一个不通过。 上面代码可以看到我们写了两条用例,基本实现了我们的需求,但是感觉还有优化的空间,于是我们改成了这样

def sum_data(x, y):
    return x + y

def test_sum_data(x, y, z):
    assert sum_data(x, y) == z


if __name__ == "__main__":
    test_sum_data(1, 2, 3)
    test_sum_data(4, 5, 6)

看起来简单了一点,但是对于接口测试来说,一个函数可能验证的点有几十条,难道我们要copy-paste test_sum_data这个函数多次吗?

大家都知道, python里有装饰器这个概念,装饰器最大的特点就是接收一个函数称为参数,然后做一些”夹带私货“的操作后再返回这个函数。 如果你不是很了解装饰器的话,也没关系,你只要记住如下转换就可以了:

#假设你有一个函数叫func, 你有一个装饰器函数叫decorator, 那么如下代码
@decorator
def func():
    pass

python解释器会解释成下面这样的语句:
func = decorator(func)

#如果你的装饰器decorator带参数怎么办?
@decorator(arg1, arg2)
def func():
    pass

#相当于:
func = decorator(arg1,arg2)(func)

了解了上面,那我们继续看,我们应该怎么用装饰器来精简我们的函数:

def sum_data(x, y):
    return x + y

def my_data(test_data):
    def wraps(func):
        def repl(x, y, z):
            for i in test_data:
                x, y, z = i
                print('func starts')
                func(x, y, z)
                print('func ends')
        return repl
    return wraps

@my_data([(1, 2, 3), (4,5,6)])
def test_sum_data(x, y , z):
    assert sum_data(x, y) == z


if __name__ == "__main__":
    test_sum_data(1, 2, 4)

#运行一遍看, 结果如下:
Traceback (most recent call last):
  File "88.py", line 22, in <module>
    test_sum_data(1, 2, 4)
  File "88.py", line 11, in repl
    func(x, y, z)
  File "88.py", line 18, in test_sum_data
    assert sum_data(x, y) == z
AssertionError

func starts
func ends
func starts

可以看到函数第2此运行的时候失败了,报的错也是assert error。 你们发现没? 函数test_sum_data的自身的实参不起作用了, 变成从my_data提供了,那么我的函数就要相应的优化下

def sum_data(x, y):
    return x + y

def my_data(test_data):
    def wraps(func):
        def repl(*args):
            for i in test_data:
                print('func starts')
                x, y , z = i
                func(x, y , z)
                print('func ends')
        return repl
    return wraps

@my_data([(1, 2, 3), (4,5,9)])
def test_sum_data(x, y , z):
    print("Your are verifing ({} + {}) == {}".format(x, y, z))
    assert sum_data(x, y) == z


if __name__ == "__main__":
    test_sum_data()

#这样我们在方法test_sum_data后面不加参数就可以了。
#注意:*args是可变参数的意思,
#它的实现实际上是个unpack,大家有兴趣可以自行了解下。

可是还有个问题,我想把这个数据驱动给我不同的函数使用怎么办?

def sum_data(x, y):
    return x + y

def is_equal(x, y):
    return x==y

def my_data(test_data):
    def wraps(func):
        def repl(*args, **kwargs):
            for i in test_data:
                print('func starts')
                func(*i, **kwargs)
                print('func ends')
        return repl
    return wraps

@my_data([(1, 2, 3), (4,5,9)])
def test_sum_data(x, y , z):
    print("Your are verifing ({} + {}) == {}".format(x, y, z))
    assert sum_data(x, y) == z

@my_data([(2,2), (1, 2)])
def test_is_equal(x, y):
    print("Your are verifing {} == {}".format(x, y))
    assert is_equal(x, y) ==1


if __name__ == "__main__":
    test_sum_data()
    test_is_equal()

#运行一下, O了。

就这样,我们一步一步实现了数据驱动的功能,实际上,如果你看过Python DataProvidor–ddt的源码,就发现,它实际上也是这么实现的。

本文的主要难点在装饰器上,而对于装饰器的理解,主要难点在闭包上,大家可以相应的搜索下,如有疑问,欢迎交流。

Hold On, 这就结束了吗? NO!

我们知道,自动化过程中我们通常会希望一个case在报告里有一个对应结果. 但对于我们实现的DataProvider,在测试报告里只会显示一条记录,太不利于我们调试了,那么,如何在测试报告里体现呢? 且听下回分解:)

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

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

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

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

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