一.UnitTest在自动化测试中的应用
UnitTest是Python语言的单元测试框架,UnitTest单元测试框架提供了创建测试用例,测试套件,和批量执行测试用例的方法。
在Python安装成功后,UnitTest单元测试框架可以直接导入使用,他属于Python的标准库;作为单元测试的框架,UnitTest单元测试框架也是对程序的最小模块进行的一种敏捷化的测试。在自动化测试中,我们虽然不需要做白盒测试,但是必须知道所使用语言的单元测试框架,这是因为后面我们测试,就会遇到用例组织的问题,虽然函数式编程和面向对象编程提供了对代码的重构,但是对于所编写的每个测试用例,不可能编写成一个函数来调用执行;利用单元测试框架,可以创建一个类,该类继承unittest的TestCase,这样可以把每个TestCase看成是一个最小的单元,由测试套件组织起来,运行时直接执行即可,同时可引入测试报告。
二、测试固件(TestFixture)
在unittest单元测试框架中,测试固件用于处理初始化的操作,例如,在对百度的搜索进行测试前,首先需要打开浏览器并且进入百度的首页;测试结束后,需要关闭浏览器;测试固件提哦功能了两种执行形式,一种是每执行一个测试用例,测试固件就会被执行一次;另外一种就不管有多少个用例,测试固件只会执行一次
1、测试固件每次均执行
unittest单元测试框架提供了名为setUp的tearDown的测试固件。下面,我们通过编写一个例子来看测试固件的执行方式,测试代码如下
执行结果如下
他的执行顺序是先执行setUp方法,在执行具体的用例,最后执行tearDown方法
2、测试固件只执行一次
钩子方法setUp和tearDown虽然经常使用,但是在自动化测试中,一个系统的测试用例多达上千条,每次都执行一次的setUp和tearDown方法会耗费大量的性能,在unittest单元测试框架中还可以使用另外一种测试固件来解决这一问题,他就是setUpClass和tearDownClass方法,该测试固件方法是类方法,需要在方法上面加装饰器@classmethod,使用该测试固件,不管有多少个用例,测试固件只执行一次,具体代码如下
结果如下:
3、两种测试固件并存
执行结果如下:
三、测试执行
在以上事例中,可以看到测试用例的执行是在主函数中,unittest调用的是main,代码如下,TestProjram还是一个类,再来看该类的构造函数,代码如下
main = TestProgram
TestProjram还是一个类,再来看该类的构造函数,代码如下
1class TestProgram(object):
2 """A command-line program that runs a set of tests; this is primarily
3 for making test modules conveniently executable.
4 """
5 # defaults for testing
6 module=None
7 verbosity = 1
8 failfast = catchbreak = buffer = progName = warnings = None
9 _discovery_parser = None
10
11 def __init__(self, module='__main__', defaultTest=None, argv=None,
12 testRunner=None, testLoader=loader.defaultTestLoader,
13 exit=True, verbosity=1, failfast=None, catchbreak=None,
14 buffer=None, warnings=None, *, tb_locals=False):
15 if isinstance(module, str):
16 self.module = __import__(module)
17 for part in module.split('.')[1:]:
18 self.module = getattr(self.module, part)
19 else:
20 self.module = module
21 if argv is None:
22 argv = sys.argv
23
24 self.exit = exit
25 self.failfast = failfast
26 self.catchbreak = catchbreak
27 self.verbosity = verbosity
28 self.buffer = buffer
29 self.tb_locals = tb_locals
30 if warnings is None and not sys.warnoptions:
31 # even if DeprecationWarnings are ignored by default
32 # print them anyway unless other warnings settings are
33 # specified by the warnings arg or the -W python flag
34 self.warnings = 'default'
35 else:
36 # here self.warnings is set either to the value passed
37 # to the warnings args or to None.
38 # If the user didn't pass a value self.warnings will
39 # be None. This means that the behavior is unchanged
40 # and depends on the values passed to -W.
41 self.warnings = warnings
42 self.defaultTest = defaultTest
43 self.testRunner = testRunner
44 self.testLoader = testLoader
45 self.progName = os.path.basename(argv[0])
46 self.parseArgs(argv)
47 self.runTests()
在unittest模块中包含的main方法,可以方便的将测试模块转变为可以运行的测试脚本。main使用unittest.TestLoader类来自动查找和加载模块内的测试用例,TestProgram类中的该部分的代码如下
在执行测试用例时候,在main方法中加入了verbosity=2,代码如下
下面解释一下verbosity部分,在verbosity中默认是1。0代表执行的测试总数和全局结果,2代表详细的信息
四、测试套件,TestSuite
1、直接执行案例
我们在func.py这个文件中定义加减乘除4个测试函数
然后在myunittest.py文件中定义我们的测试代码,这里用到了断言,我们后面会介绍
执行结果如下:
2、添加案例到测试套件中
上述简单的测试会产生两个问题,可不可以控制test测试用例的执行顺序?若不想执行某个测试用例,有没有办法可以跳过?
对于执行顺序,默认按照test的 A-Z、a-z的方法执行。若要按自己编写的用例的先后关系执行,需要用到testSuite。
把多个测试用例集合起来,一起执行,就是testSuite。testsuite还可以包含testsuite。
一般通过addTest()或者addTests()向suite中添加。case的执行顺序与添加到Suite中的顺序是一致的。
如果用到测试套件TestSuite,则需要先写好测试代码,但是先不要执行
我们同样在myunittest.py文件中定义我们的测试代码
我们在test_suit.py文件中引入测试案例,然后通过TestSuite类的addTests方法把测试用例添加到测试套件中
以上的案例我们是添加一个文件的测试案例,我们同样可以添加多个文件中的案例到一个测试套件中,然后执行这个测试套件即可
上面的执行方式是输出结果到控制台,我们也可以输出结果到文件中
3、直接添加测试类到测试套件中
案例一个一个添加还是比较麻烦,我们可以直接添加一个测试类到测试套件中
利用下面的方法加载一个测试类
unittest.TestLoader().loadTestsFromTestCase(t3)
4、直接加载一个模块到测试套件中,如果这个模块中有多个类,则会把所有的类的测试案例加载到测试套件中
unittest.TestLoader().loadTestsFromModule(myunittest)
截图如下
5、通过案例名称添加案例到测试套件中
test_cases1 = unittest.TestLoader().loadTestsFromName('test1.myunittest2.Test3.test_mi
截图如下
五、忽略执行案例
在实际的项目中,有些案例我们可能暂时不需要执行,如果有这样的问题,我们该怎么办,unittest框架已经为我们提供了解决方案
1、无条件跳过该案例,用该装饰器修饰要执行的案例,则该案例会被忽略不执行
@unittest.skip("do not exec")
@unittest.skip("do not exec") # 无条件跳过执行该案例 def test_add(self): self.assertEqual(3,func.add(1,2))
2、满足某个条件才跳过该案例
@unittest.skipIf(4 > 3,"2 > 3 do not exec")
@unittest.skipIf(4 > 3,"2 > 3 do not exec") # 满足某个条件才跳过执行 def test_minus(self): self.assertEqual(4,func.minus(5,1))
3、不满足某个条件才跳过案例
@unittest.skipUnless(4 < 3,"hahah")
@unittest.skipUnless(4 < 3,"hahah") # 不满足某个条件才跳过执行 def test_multi(self): self.assertEqual(4,func.multi(2,2))
4、我们也可以在案例里面定义忽略执行这条案例
def test_divide(self): self.skipTest("wydd") self.assertEqual(10,func.divide(100,10))
六、断言
断言就是判断实际测试结果与预期结果是否一致,一致则测试通过,否则失败。
因此,在自动化测试中,无断言的测试用例是无效的,这是因为当一个功能自动化已经全部实现,在每次版本迭代中执行测试用例时,执行的结果必须是权威的,也就是说自动化测试用例执行结果应该无功能性或者逻辑性问题,在自动化测试中最忌讳的就是自动化测试的用例虽然是通过的,但是被测试的功能却是存在问题的,自动化测试用例经常应用在回归测试中,发现的问题不是特别多,如果测试结果存在功能上的问题,则投入了人力去做的自动化参数就没有多大的意义了,所以每一个测试用例必须要有断言;
在测试的结果中只有两种可能,一种是执行通过,另外一种是执行失败,也就是功能存在问题,在TestCase类中提供了assert方法来检查和报告失败,常用的方法如下
self.assertEqual(3,func.add(1,2))
# 判断是否相等
self.assertNotEqual()
# 判断是否不等于
self.assertTrue()
# 判断布尔值是否为True
self.assertFalse()
# 判断布尔值是否为False
self.assertIs()
# 判断类型是否相同
self.assertIsNot()
# 判断类型是否不同
self.assertIsNone()
# 判断是否为None
self.assertIsNotNone()
# 判断是否不为None
self.assertIn()
# 判断在某个范围内
self.assertNotIn()
# 判断是否不在某个范围内
self.assertIsInstance()
# 判断是否为某个类的实例
self.assertNotIsInstance()