数据驱动测试¶
所谓数据驱动测试,即是将测试数据和测试用例分离,可以实现一份测试用例跑多份的测试数据,在一些业务场景的测试下可以大大提升测试用例开发和维护的成本。
数据驱动测试用例¶
先看一个简单的例子,小E需要设计一个针对登录的测试用例:
- 输入“111”登录QQ,登录失败,提示非法帐号
- 输入“”登录QQ,登录失败,提示非法帐号
- 输入“11111111111111111”登录QQ,登录失败,提示非法帐号
- 输入“$%^&#”登录QQ,登录失败,提示非法帐号
如果需要实现对应的自动化测试用例,则可能会写类似下面的代码:
from testbase.testcase import TestCase
class InvalidUinTest1(TestCase):
'''非法测试号码1
'''
owner = "foo"
status = TestCase.EnumStatus.Ready
priority = TestCase.EnumPriority.Normal
timeout = 1
def run_test(self):
qq = QQApp()
login = LoginPanel(qq)
login['uin'] = "111"
login['passwd'] = "test123"
login['login'].click()
self.assertEqual(login['tips'].text, "非法帐号")
class InvalidUinTest2(TestCase):
'''非法测试号码2
'''
owner = "foo"
status = TestCase.EnumStatus.Ready
priority = TestCase.EnumPriority.Normal
timeout = 1
def run_test(self):
qq = QQApp()
login = LoginPanel(qq)
login['uin'] = ""
login['passwd'] = "test123"
login['login'].click()
self.assertEqual(login['tips'].text, "非法帐号")
class InvalidUinTest3(TestCase):
'''非法测试号码3
'''
owner = "foo"
status = TestCase.EnumStatus.Ready
priority = TestCase.EnumPriority.Normal
timeout = 1
def run_test(self):
qq = QQApp()
login = LoginPanel(qq)
login['uin'] = "11111111111111111"
login['passwd'] = "test123"
login['login'].click()
self.assertEqual(login['tips'].text, "非法帐号")
class InvalidUinTest4(TestCase):
'''非法测试号码4
'''
owner = "foo"
status = TestCase.EnumStatus.Ready
priority = TestCase.EnumPriority.Normal
timeout = 1
def run_test(self):
qq = QQApp()
login = LoginPanel(qq)
login['uin'] = "$%^&#"
login['passwd'] = "test123"
login['login'].click()
self.assertEqual(login['tips'].text, "非法帐号")
if __name__ == '__main__':
InvalidUinTest1().debug_run()
InvalidUinTest2().debug_run()
InvalidUinTest3().debug_run()
InvalidUinTest4().debug_run()
从上面的代码看出,用户的逻辑基本上是类似的,每个用例几乎只有一点点的差异,特别是如果测试的场景变多了,用例维护起来更麻烦。
这里我们就可以用数据驱动用例来解决这个问题,使用数据驱动修改后的用例:
from testbase.testcase import TestCase
from testbase import datadrive
testdata = [
"111",
"",
"11111111111111111",
"$%^&#",
]
@datadrive.DataDrive(testdata)
class InvalidUinTest(TestCase):
'''非法测试号码
'''
owner = "foo"
status = TestCase.EnumStatus.Ready
priority = TestCase.EnumPriority.Normal
timeout = 1
def run_test(self):
qq = QQApp()
login = LoginPanel(qq)
login['uin'] = self.casedata
login['passwd'] = "test123"
login['login'].click()
self.assertEqual(login['tips'].text, "非法帐号")
if __name__ == '__main__':
InvalidUinTest().debug_run()
如果执行以上的代码,其输出的结果是和前面四个测试用例的执行结果是一致的,但是这里却只有一个用例,这个就是数据驱动测试用例的强大之处。
上面的数据驱动测试用例和一般测试用例的主要区别在两点:
- 测试用例类增加了修饰器“
testbase.datadrive.DataDrive
”,修饰器接受一个参数来指定对应的测试数据- 测试用例通过casedata属性获取测试数据
测试数据¶
测试数据通过修饰器“testbase.datadrive.DataDrive
”指定,目前支持两个格式的数据:
- list和list兼容类型
- dict和dict兼容类型
list类型测试数据¶
上面的InvalidUinTest使用的就是list类型的测试数据,对于list类型的数据,QTA会将list的每一个元素生成对应的一个测试用例,并将该元素赋值给对应的测试用例的casedata属性。
例如测试数据为:
@datadrive.DataDrive(["AA", 1234234, {"xx":"XX"}, True])
class HelloDataTest(TestCase):
pass
则生成的四个测试用例对应的casedata分别为:
"AA"
1234234
{"xx":"XX"}
True
dict类型测试数据¶
数据驱动也支持dict类型的测试数据,QTA会讲dict类型的所有值生成对应的一个测试用例,并将该值赋给对应的测试用例的casedata属性。
例如测试数据为:
@datadrive.DataDrive({
"A": "AA",
"B": 1234234,
"C": {"xx":"XX"},
"D": True
})
class HelloDataTest(TestCase):
pass
则生成的四个测试用例对应的casedata分别为:
"AA"
1234234
{"xx":"XX"}
True
但dict的键在这里似乎没什么用处?
调试数据驱动用例¶
数据驱动用例的调试时,可以和一般用例一样使用debug_run接口,例如:
if __name__ == '__main__':
HelloDataTest().debug_run()
使用debug_run调试时,会执行全部数据驱动的用例,如果需要针对单个数据进行调试,可以使用debug_run_one接口:
if __name__ == '__main__':
HelloDataTest().debug_run_one()
以上的待会随机使用一个数据驱动用例进行执行调试,如果需要指定某个数据的用例进行调试:
if __name__ == '__main__':
HelloDataTest().debug_run_one("B")
这里的“B”是数据驱动的数据的名字,按照以上HelloDataTest的定义,的对应的数据就是1234234。
设置单个数据驱动用例属性¶
由于数据驱动方式生成的多个测试用例,虽然测试数据不同,但是由于都是同一个Python类,因此都具有相同的测试用例属性(优先级、状态、描述等)。 如果需要设置单个数据对应的生成的用例的属性,则要求测试数据必须是dictionary的类型,测试用例的属性可以在dictionary数据中的“__attrs__”键值来定义:
@datadrive.DataDrive([
{"data": 121231, "__attrs__": {"priority": TestCase.EnumPriority.Low } },
{"data": True, "__attrs__": {"__doc__": "使用布尔类型数据测试"} },
])
class HelloDataTest(TestCase):
"""示例用例
"""
owner = "xxx"
timeout = 1
priority = TestCase.EnumPriority.BVT
status = TestCase.EnumStatus.Ready
def run_test(self):
self.log_info(self.priority)
self.log_info(self.test_doc)
以上的用例,在根据测试用例生成的用例,会具备不一样的属性,比如第一个数据驱动用例的优先级会变为“TestCase.EnumPriority.Low”,但第二个数据对应的用例则继承原本用例类的优先级“TestCase.EnumPriority.BVT”。
除了上面例子中的属性,用户可以设置以下的用例属性:
* priority
* status
* owner
* timeout
* tags
* __doc__
注解
此方式不支持全局数据驱动
管理数据驱动测试用例¶
QTA对于每个测试用例,都有一个唯一的名字;由于数据驱动把一个测试用例对应数据生成了多个测试用例,所以QTA对于每个数据驱动生成的用例的名字也是不一样的。
假设一个数据驱动的用例footest/cat/eat.py:
@datadrive.DataDrive(["fish", "mouse", "apple"])
class EatTest(TestCase):
#这里省略相关代码
pass
如果我们参考《管理测试用例》使用TestLoader来加载这块测试用例:
from testbase.loader import TestLoader
loader = TestLoader()
for it in loader.load("zootest.cat.eat"):
print(it.test_name)
执行结果如下:
zootest.cat.eat.EatTest/0
zootest.cat.eat.EatTest/1
zootest.cat.eat.EatTest/2
可以看到每个用例后面都有一个后缀,表示对应的list的索引值。
这个是list类型的例子,如果是dict类型:
@datadrive.DataDrive({
"fish": "fish",
"mouse": "mouse",
"apple": "apple",
})
class EatTest(TestCase):
#这里省略相关代码
pass
则TestLoader的执行结果如下:
zootest.cat.eat.EatTest/fish
zootest.cat.eat.EatTest/mouse
zootest.cat.eat.EatTest/apple
之前的list的索引变成了dict键。
其实TestLoader也支持加载一个单独的数据驱动用例:
from testbase.loader import TestLoader
loader = TestLoader()
for it in loader.load("zootest.cat.eat/fish"):
print(it.test_name)
则TestLoader的执行结果如下:
zootest.cat.eat.EatTest/fish
全局数据驱动测试¶
数据驱动用例需要我们去修改测试用例,并为每个测试用例都增加修饰器和通过casedata访问数据,但是有没有可能在不修改测试用例的情况下,对全部的测试用例都进行数据驱动测试呢?比如对于后台测试,通过配置一份测试服务器的IP列表作为测试数据,然后对全部用例都以这份IP列表来生成对应的N个用例。答案就是全局数据驱动用例。
设置全局数据驱动需要修改项目的settings.py文件,增加下面两个配置:
DATA_DRIVE = True
DATA_SOURCE = 'test/data/server.py'
注解
settings.py配置的更多使用方法,请参考《配置测试项目》
第一个配置表示打开全局数据驱动,第二个配置指定一个py文件作为数据源,如server.py:
DATASET = [
"11.22.11.11",
"11.22.11.12",
"11.22.11.13",
"11.22.11.14",
]
数据py文件只需要定义一个DATASET模块变量,变量类型要求是list或者dict类型,格式和作用和前面的数据驱动用例DataDrive参数是一样的。
通过以上配置之后,本地调用debug_run调试脚本,可以看到每个用例都会被执行4次,且每次的casedata数据分别为DATASET变量定义的数据。
如果数据格式比较简单,也可以直接内嵌在settings.py中,这个时候DATA_SOURCE即表示数据源,同上面配置等价的配置如下:
DATA_DRIVE = True
DATA_SOURCE = [
"11.22.11.11",
"11.22.11.12",
"11.22.11.13",
"11.22.11.14",
]
注解
当测试用例已经有修饰器DataDrive,但同时配置了全局数据驱动,这个时候全局数据驱动对于这个用例是无效的,这个用例还是只会通过DataDrive生成数据驱动测试用例。