iTesting软件测试知识分享

测试框架--教你用Python实现数据驱动2

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

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

1
2
3
4
5
def sum_data(x, y):
return x + y
if __name__ == "__main__":
assert sum_data(1, 2) == 3
assert sum_data(4, 5) ==8

正常来说, 要验证这个函数的实现正确与否,最基本的两个检查点一定是一个通过一个不通过。 上面代码可以看到我们写了两条用例,基本实现了我们的需求,但是感觉还有优化的空间,于是我们改成了这样
1
2
3
4
5
6
7
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里有装饰器这个概念,装饰器最大的特点就是接收一个函数称为参数,然后做一些”夹带私货“的操作后再返回这个函数。
如果你不是很了解装饰器的话,也没关系,你只要记住如下转换就可以了:
1
2
3
4
#假设你有一个函数叫func, 你有一个装饰器函数叫decorator, 那么如下代码
@decorator
def func():
pass

python解释器会解释成下面这样的语句:
1
2
3
4
5
6
7
func = decorator(func)
#如果你的装饰器decorator带参数怎么办?
@decorator(arg1, arg2)
def func():
pass
#相当于:
func = decorator(arg1,arg2)(func)

了解了上面,那我们继续看,我们应该怎么用装饰器来精简我们的函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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提供了,那么我的函数就要相应的优化下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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,大家有兴趣可以自行了解下。

可是还有个问题,我想把这个数据驱动给我不同的函数使用怎么办?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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,在测试报告里只会显示一条记录,太不利于我们调试了。
那么,如何在测试报告里体现呢? 且听下回分解:)

🐶 您的支持将鼓励我继续创作 🐶
-------------评论, 吐槽, 学习交流,请关注微信公众号 iTesting-------------
iTesting wechat
扫码关注,跟作者互动

本文标题:测试框架--教你用Python实现数据驱动2

文章作者:iTesting

发布时间:2018年09月18日 - 23:09

最后更新:2018年12月28日 - 00:12

原始链接:http://www.helloqa.com/2018/09/18/测试框架/测试框架--教你用Python实现数据驱动2/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。