设计测试用例¶
最简单的测试用例¶
下面我们来编写第一个QTA测试用例,在测试项目中新建一个hello.py:
from testbase.testcase import TestCase
class HelloTest(TestCase):
'''第一个测试用例
'''
owner = "foo"
status = TestCase.EnumStatus.Ready
priority = TestCase.EnumPriority.Normal
timeout = 1
def run_test(self):
#---------------------------
self.start_step("第一个测试步骤")
#---------------------------
self.log_info("hello")
可以看到,这个最简单的测试用例包括以下主要的部分:
一个测试用例就是一个Python类,类的名称就是测试用例名,类的DocString就是测试用例的简要说明文档。注意DocString对于QTA测试用例而言是必要的,否则会导致执行用例失败。
测试用例类包括四个必要的属性:
- owner,测试用例负责人,必要属性。
- status,测试用例状态,必要属性。目前测试用例有五种状态:Design、Implement、Review、Ready、Suspend。
- priority,测试用例优先级,必要属性。目前测试用例有四种优先级:BVT、High、Normal和Low。
- timeout,测试用例超时时间,必要属性,单位为分钟。超时时间用于指定测试用例执行的最长时间,如果测试用例执行超过此时间,执行器会停止用例执行,并认为用例执行超时,测试不通过。一般来说,不建议这个时间设置得太长,如果用例需要比较长的执行时间,可以考虑拆分为多个测试用例。
- run_test函数:这个是测试逻辑的代码,每个测试用例只有一个唯一的run_test函数;但每个测试用例可以划分为多个测试步骤,测试步骤以start_step函数调用来分隔。
以上的测试用例并没有任何测试逻辑,只是调用接口打印一行日志。
注解
由于历史的原因,QTA很多接口的函数有两种代码风格的版本,比如上面的run_test、log_info,就有对应的mixedCase的版本runTest、logInfo。一般情况下,建议和测试项目已有的代码使用一致的风格的接口,如果是新项目,推荐使用lower_with_under风格的接口。
调试执行¶
测试用例编写后,需要进行调试执行。需要在hello.py中增加以下的代码:
if __name__ == '__main__':
HelloTest().debug_run()
如果使用的是IDE,在eclipse中,通过“CTRL + F11”快捷键执行当前hello.py脚本,可以看到输出如下:
============================================================
测试用例:HelloTest 所有者:foo 优先级:Normal 超时:1分钟
============================================================
----------------------------------------
步骤1: 第一个测试步骤
INFO: hello
============================================================
测试用例开始时间: 2015-04-27 12:51:59
测试用例结束时间: 2015-04-27 12:51:59
测试用例执行时间: 00:00:0.00
测试用例步骤结果: 1:通过
测试用例最终结果: 通过
============================================================
如果没有使用IDE,可以通过manage.py执行单个用例:
$ python manage.py runscript footest/hello.py
在命令行窗口可以看到一样的执行输出:
============================================================
测试用例:HelloTest 所有者:foo 优先级:Normal 超时:1分钟
============================================================
----------------------------------------
步骤1: 第一个测试步骤
INFO: hello
============================================================
测试用例开始时间: 2015-04-27 12:51:59
测试用例结束时间: 2015-04-27 12:51:59
测试用例执行时间: 00:00:0.00
测试用例步骤结果: 1:通过
测试用例最终结果: 通过
============================================================
测试用例标签¶
测试用例除了owner、timeout、status和priority之外,还有一个自定义的标签属性“tags”。测试标签的作用是,在批量执行用例的时候,用来指定或排除对应的测试用例,相关详情可以参考《执行测试》。
设置标签的方式十分简单:
from testbase.testcase import TestCase
class HelloTest(TestCase):
'''第一个测试用例
'''
owner = "foo"
status = TestCase.EnumStatus.Ready
priority = TestCase.EnumPriority.Normal
timeout = 1
tags = "Demo"
def run_test(self):
#---------------------------
self.start_step("第一个测试步骤")
#---------------------------
self.log_info("hello")
标签支持一个或多个,下面的例子也是正确的:
from testbase.testcase import TestCase
class HelloTest(TestCase):
'''第一个测试用例
'''
owner = "foo"
status = TestCase.EnumStatus.Ready
priority = TestCase.EnumPriority.Normal
timeout = 1
tags = "Demo", "Help"
def run_test(self):
#---------------------------
self.start_step("第一个测试步骤")
#---------------------------
self.log_info("hello")
需要注意的是,测试用例标签经过框架处理后,会变成set类型,比如上面的用例:
assert HelloTest.tags == set("Demo", "Help")
测试环境初始化和清理¶
在前面的例子中,我们在测试用例类的run_test实现了测试的主要逻辑,这里我们引入两个新的接口pre_test和post_test。
假设我们的用例需要临时配置一个本地host域名,示例代码如下:
from testbase.testcase import TestCase
class EnvTest1(TestCase):
'''环境构造测试
'''
owner = "foo"
status = TestCase.EnumStatus.Ready
priority = TestCase.EnumPriority.Normal
timeout = 1
def run_test(self):
_add_host("www.qq.com", "11.11.12.12")
# main test logic here
# ...
_del_host("www.qq.com", "11.11.12.12")
以上的代码在逻辑,在用例正常执行完成的情况下是完全正确的,但是这里存在一个问题,就是当run_test测试过程中,由于测试目标bug或者脚本问题导致run_test异常终止,则可能导致host配置没有删除,则可能影响到后面的测试用例。如何解决这个问题呢?QTA为此提供了post_test接口。
下面是使用post_test接口的新版本的测试用例代码:
from testbase.testcase import TestCase
class EnvTest2(TestCase):
'''环境构造测试
'''
owner = "foo"
status = TestCase.EnumStatus.Ready
priority = TestCase.EnumPriority.Normal
timeout = 1
def run_test(self):
_add_host("www.qq.com", "11.11.12.12")
# main test logic
# ...
def post_test(self):
super(EnvTest2, self).post_test()
_del_host("www.qq.com", "11.11.12.12")
QTA执行用例的接口是先执行run_test,然后执行post_test;而且即使测试用例执行run_test中发生异常,仍会执行post_test,这样就保证了测试环境的清理操作。
注解
虽然使用post_test可以保证清理环境,但是还是要注意清理环境的逻辑要尽量简单,否则清理环境时发生异常,也会导致清理动作未完成。
和post_test对应,QTA还提供了pre_test接口,从名字上看以看出,pre_test的作用主要是用于测试环境的构造和初始化,下面是使用pre_test的例子:
class EnvTest3(TestCase):
'''环境构造测试
'''
owner = "foo"
status = TestCase.EnumStatus.Ready
priority = TestCase.EnumPriority.Normal
timeout = 1
def pre_test(self):
_add_host("www.qq.com", "11.11.12.12")
super(EnvTest3, self).pre_test()
def run_test(self):
# main test logic
# ...
pass
def post_test(self):
super(EnvTest3, self).post_test()
_del_host("www.qq.com", "11.11.12.12")
QTA会依照以下顺序执行测试用例的三个接口:
- pre_test
- run_test
- post_test
且任意一个接口执行异常,QTA仍然会执行下一个接口。
注解
由于历史原因,QTA还提供另一套代码风格的接口preTest、runTest和postTest,建议测试用例编写时选择测试项目存量代码统一的代码风格,如果是新的测试项目还是建议使用lower_with_under的代码风格。
警告
在一个测试用例中仅支持一套代码风格的接口,QTA选择接口的代码风格是基于run_test/runTest选择的风格为主,也就是说如果用例定义了runTest,则只会执行preTest和postTest,但不会执行pre_test和post_test。当run_test和runTest两个接口都存在的时候,QTA优先选择run_test接口来执行。
pre_test这个接口的一个作用是可以提高测试用例代码的复用,比如以下的例子:
from testbase.testcase import TestCase
class EnvTestBase(TestCase):
def pre_test(self):
super(EnvTestBase, self).post_test()
_add_host("www.qq.com", "11.11.12.12")
def post_test(self):
super(EnvTestBase, self).post_test()
_del_host("www.qq.com", "11.11.12.12")
class EnvTest4(EnvTestBase):
'''环境构造测试
'''
owner = "foo"
status = TestCase.EnumStatus.Ready
priority = TestCase.EnumPriority.Normal
timeout = 1
def run_test(self):
# code 1
pass
class EnvTest5(EnvTestBase):
'''环境构造测试
'''
owner = "foo"
status = TestCase.EnumStatus.Ready
priority = TestCase.EnumPriority.Normal
timeout = 1
def run_test(self):
# code 2
pass
可以看到EnvTest4和EnvTest5的基类都是为EnvTestBase,也就是他们本身会继承基类的pre_test和post_test的实现,因此也会进行环境的初始化和清理的动作。
注解
可以看到EnvTestBase的pre_test和post_test方法都调用的super接口,对于Python语言的含义表示的是调用基类的方法,虽然不是必定需要的,但是大部分情况下还是推荐这样做;因为这样做可以保证基类的初始化和清理的接口会被执行。