终于等到十一,有时间写博客了,准备利用十一这几天的假期把这个系列的博客写完python
该系列文章本人准备写三篇博客数据库
第一篇:介绍python自动化测试框架unittestdjango
第二篇:介绍django框架+request库实现接口测试编程
第三篇:介绍利用Jenkins实现持续集成浏览器
今天进入第一篇,unittest框架介绍框架
unittest是python语言的单元测试框架,在python的官方文档中,对unittest单元测试框架进行了详细的介绍,感兴趣的读者能够到https://www.python.org/doc
网站去了解;本篇博客重点介绍unittest单元测试框架在自动化测试中的应用less
unittest单元测试框架提供了建立测试用例,测试套件,和批量执行测试用例的方法,在python安装成功后,unittest单元测试框架能够直接导入使用,他属于
python的标准库;做为单元测试的框架,unittest单元测试框架也是对程序的最小模块进行的一种敏捷化的测试。在自动化测试i中,咱们虽然不须要作白盒测试,
可是必须知道所使用语言的单元测试框架,这是由于后面咱们测试,就会遇到用例组织的问题,虽然函数式编程和面向对象编程提供了对代码的重构,可是对于所
编写的每一个测试用例,不可能编写成一个函数来调用执行;利用单元测试框架,能够建立一个类,该类继承unittest的TestCase,这样能够把每一个TestCase当作是
一个最小的单元,由测试套件组织起来,运行时直接执行便可,同时可引入测试报告。unittest各个组件的关系若是ide
TestCase------------------------------->TestFixture(测试固件)
|
|
|
|
|
|
|
|
|
TestSuite(测试套件)----------------------->TestRunner(测试执行)-------------------->TestReport(测试报告)函数式编程
# TestCase
# 类,必需要继承unittest.TestCase
# 一个类class继承 unittest.TestCase,就是一个测试用例。一个TestCase的实例就是一个测试用例,就是一个完整的测试流程。
# 包括测试前环境准备setUp()|setUpClass()、执行代码run()、测试环境后的还原tearDown()|tearDownClass()。
# 继承自unittest.TestCase的类中,测试方法的名称要以test开头。且只会执行以test开头定义的方法(测试用例)。
在unittest单元测试框架中,测试固件用于处理初始化的操做,例如,在对百度的搜索进行测试前,首先须要打开浏览器而且进入百度的首页;测试结束后,
须要关闭浏览器;测试固件提哦功能了两种执行形式,一种是每执行一个测试用例,测试固件就会被执行一次;另一种就无论有多少个用例i,测试固件只会执
行一次函数
# 用于一个测试环境的准备和销毁还原。
# 当测试用例每次执行以前须要准备测试环境,每次测试完成后还原测试环境,好比执行前链接数据库、打开浏览器等,执行完成后须要还原数据库、关闭浏览器等操做。
# 这时候就能够启用testfixture。
# setUp():准备环境,执行每一个测试用例的前置条件;
# tearDown():环境还原,执行每一个测试用例的后置条件;
# setUpClass():必须使用@classmethod装饰器,全部case执行的前置条件,只运行一次;
# tearDownClass():必须使用@classmethod装饰器,全部case运行完后只运行一次;
unittest单元测试框架提供了名为setUp的tearDown的测试固件。下面,咱们经过编写一个例子来看测试固件的执行方式,测试代码以下
1 import unittest 2 3 class Test1(unittest.TestCase): 4 5 # 测试固件以前置条件 6 def setUp(self): 7 print("这是前置条件") 8 9 # 测试固件以后置条件 10 def tearDown(self): 11 print("这是后置条件") 12 13 def test_case1(self): 14 print("test_case1") 15 16 def test_case2(self): 17 print("test_case2") 18 19 20 if __name__ == '__main__': 21 unittest.main(verbosity=2)
执行结果以下
他的执行顺序是先执行setUp方法,在执行具体的用例,最后执行tearDown方法
钩子方法setUp和tearDown虽然常用,可是在自动化测试中,一个系统的测试用例多达上千条,每次都执行一次的setUp和tearDown方法会耗费大量的性能,
在unittest单元测试框架中还可使用另一种测试固件来解决这一问题,他就是setUpClass和tearDownClass方法,该测试固件方法是类方法,须要在方法上
面加装饰器@classmethod,使用该测试固件,无论有多少个用例,测试固件只执行一次,具体代码以下
1 import unittest 2 3 class Test1(unittest.TestCase): 4 # def setUp(self): 5 # print("这是前置条件") 6 # 7 # def tearDown(self): 8 # print("这是后置条件") 9 10 @classmethod 11 def setUpClass(cls): 12 print("这是类方法前置条件") 13 14 @classmethod 15 def tearDownClass(cls): 16 print("这是类方法后置条件") 17 18 def test_case1(self): 19 print("test_case1") 20 21 def test_case2(self): 22 print("test_case2") 23 24 25 if __name__ == '__main__': 26 unittest.main(verbosity=2)
结果以下
import unittest class Test1(unittest.TestCase): def setUp(self): print("这是前置条件") def tearDown(self): print("这是后置条件") @classmethod def setUpClass(cls): print("这是类方法前置条件") @classmethod def tearDownClass(cls): print("这是类方法后置条件") def test_case1(self): print("test_case1") def test_case2(self): print("test_case2") if __name__ == '__main__': unittest.main(verbosity=2)
执行结果以下
结果代表,先执行被@classmethod装饰器装饰的测试固件,在执行普通的测试固件
在以上事例中,能够看到测试用例的执行是在主函数中,unittest调用的是main,代码以下,TestProjram仍是一个类,再来看该类的构造函数,代码以下
main = TestProgram
TestProjram仍是一个类,再来看该类的构造函数,代码以下
class TestProgram(object): """A command-line program that runs a set of tests; this is primarily for making test modules conveniently executable. """ # defaults for testing module=None verbosity = 1 failfast = catchbreak = buffer = progName = warnings = None _discovery_parser = None def __init__(self, module='__main__', defaultTest=None, argv=None, testRunner=None, testLoader=loader.defaultTestLoader, exit=True, verbosity=1, failfast=None, catchbreak=None, buffer=None, warnings=None, *, tb_locals=False): if isinstance(module, str): self.module = __import__(module) for part in module.split('.')[1:]: self.module = getattr(self.module, part) else: self.module = module if argv is None: argv = sys.argv self.exit = exit self.failfast = failfast self.catchbreak = catchbreak self.verbosity = verbosity self.buffer = buffer self.tb_locals = tb_locals if warnings is None and not sys.warnoptions: # even if DeprecationWarnings are ignored by default # print them anyway unless other warnings settings are # specified by the warnings arg or the -W python flag self.warnings = 'default' else: # here self.warnings is set either to the value passed # to the warnings args or to None. # If the user didn't pass a value self.warnings will # be None. This means that the behavior is unchanged # and depends on the values passed to -W. self.warnings = warnings self.defaultTest = defaultTest self.testRunner = testRunner self.testLoader = testLoader self.progName = os.path.basename(argv[0]) self.parseArgs(argv) self.runTests()
在unittest模块中包含的main方法,能够方便的将测试模块转变为能够运行的测试脚本。main使用unittest.TestLoader类来自动查找和加载模块内的测试用例,TestProgram类中的该部分的代码以下
def createTests(self): if self.testNames is None: self.test = self.testLoader.loadTestsFromModule(self.module) else: self.test = self.testLoader.loadTestsFromNames(self.testNames, self.module)
在执行测试用例时候,在main方法中加入了verbosity=2,代码以下
if __name__ == '__main__': unittest.main(verbosity=2)
下面解释一下verbosity部分,在verbosity中默认是1。0表明执行的测试总数和全局结果,2表明详细的信息
# TestSuite
# 上述简单的测试会产生两个问题,可不能够控制test测试用例的执行顺序?若不想执行某个测试用例,有没有办法能够跳过?
# 对于执行顺序,默认按照test的 A-Z、a-z的方法执行。若要按本身编写的用例的前后关系执行,须要用到testSuite。
# 把多个测试用例集合起来,一块儿执行,就是testSuite。testsuite还能够包含testsuite。
# 通常经过addTest()或者addTests()向suite中添加。case的执行顺序与添加到Suite中的顺序是一致的。
咱们在func.py这个文件中定义加减乘除4个测试函数
#Auther Bob
#--*--conding:utf-8 --*--
def add(a,b): return a + b def minus(a,b): return a - b def multi(a,b): return a * b def divide(a,b): return a / b
而后在myunittest.py文件中定义咱们的测试代码,这里用到了断言,咱们后面会介绍
from test1 import func class Test2(unittest.TestCase): def setUp(self): print("前置条件") def tearDown(self): print("后置条件") def test_add(self): self.assertEqual(3,func.add(1,2)) def test_minus(self): self.assertEqual(4,func.minus(5,1)) def test_multi(self): self.assertEqual(4,func.multi(2,2)) def test_divide(self): self.assertEqual(10,func.divide(100,10)) if __name__ == '__main__': unittest.main(verbosity=2)
执行结果以下
上述简单的测试会产生两个问题,可不能够控制test测试用例的执行顺序?若不想执行某个测试用例,有没有办法能够跳过?
对于执行顺序,默认按照test的 A-Z、a-z的方法执行。若要按本身编写的用例的前后关系执行,须要用到testSuite。
把多个测试用例集合起来,一块儿执行,就是testSuite。testsuite还能够包含testsuite。
通常经过addTest()或者addTests()向suite中添加。case的执行顺序与添加到Suite中的顺序是一致的。
若是用到测试套件TestSuite,则须要先写好测试代码,可是先不要执行
咱们一样在myunittest.py文件中定义咱们的测试代码
from test1 import func class Test3(unittest.TestCase): def setUp(self): print("前置条件") def tearDown(self): print("后置条件") def test_add(self): self.assertEqual(3,func.add(1,2)) def test_minus(self): self.assertEqual(4,func.minus(5,1)) def test_multi(self): self.assertEqual(4,func.multi(2,2)) def test_divide(self): self.assertEqual(10,func.divide(100,10))
咱们在test_suit.py文件中引入测试案例,而后经过TestSuite类的addTests方法把测试用例添加到测试套件中
import unittest from test1.myunittest import Test3 # from test1.myunittest2 import Test3 as t3 if __name__ == '__main__': # 输出信息到控制台 # 实例化一个TestSuite类 suite = unittest.TestSuite() # 把须要执行的案例放在一个list中 tests = [Test3("test_add"), Test3("test_minus"), Test3("test_multi"), Test3("test_divide")] # 把案例添加到实例化好的测试套件中 suite.addTests(tests) # t = [t3("test_add"), t3("test_minus"), t3("test_multi"), t3("test_divide")] # suite.addTests(tests) # 实例化一个参数执行类 runner = unittest.TextTestRunner(verbosity=2) # 测试执行类的实例执行测试套件 runner.run(suite)
以上的案例咱们是添加一个文件的测试案例,咱们一样能够添加多个文件中的案例到一个测试套件中,而后执行这个测试套件便可
import unittest from test1.myunittest import Test3 from test1.myunittest2 import Test3 as t3 if __name__ == '__main__': # 输出信息到控制台 # 实例化一个TestSuite类 suite = unittest.TestSuite() # 把须要执行的案例放在一个list中 tests = [Test3("test_add"), Test3("test_minus"), Test3("test_multi"), Test3("test_divide")] # 把案例添加到实例化好的测试套件中 suite.addTests(tests) # 添加另一个文件中的测试案例到测试套件中 t = [t3("test_add"), t3("test_minus"), t3("test_multi"), t3("test_divide")] suite.addTests(t) # 实例化一个参数执行类 runner = unittest.TextTestRunner(verbosity=2) # 测试执行类的实例执行测试套件 runner.run(suite)
上面的执行方式是输出结果到控制台,咱们也能够输出结果到文件中
import unittest from test1.myunittest import Test3 from test1.myunittest2 import Test3 as t3 if __name__ == '__main__': # 输出信息到txt文件中 suite = unittest.TestSuite() tests = [Test3("test_add"), Test3("test_minus"), Test3("test_multi"), Test3("test_divide")] suite.addTests(tests) t = [t3("test_add"), t3("test_minus"), t3("test_multi"), t3("test_divide")] suite.addTests(t) with open('UnittestTextReport.txt', 'a') as f: runner = unittest.TextTestRunner(stream=f, verbosity=2) runner.run(suite)
案例一个一个添加仍是比较麻烦,咱们能够直接添加一个测试类到测试套件中
利用下面的方法加载一个测试类
unittest.TestLoader().loadTestsFromTestCase(t3)
import unittest from unittest import TestLoader from test1 import myunittest from test1.myunittest2 import Test3 as t3 if __name__ == '__main__': suite = unittest.TestSuite() loader = TestLoader() test_cases1 = unittest.TestLoader().loadTestsFromTestCase(t3) # 参数是一个类,而这个类必须是unittest.TestCase的子类或者孙类 suite.addTests(test_cases1) runner = unittest.TextTestRunner(verbosity=2) runner.run(suite)
unittest.TestLoader().loadTestsFromModule(myunittest)
import unittest from unittest import TestLoader from test1 import myunittest from test1.myunittest2 import Test3 as t3 if __name__ == '__main__': suite = unittest.TestSuite() loader = TestLoader() test_cases1 = unittest.TestLoader().loadTestsFromModule(myunittest) # 参数是一个模块,会把这个模块里的全部case加载进来 suite.addTests(test_cases1) runner = unittest.TextTestRunner(verbosity=2) runner.run(suite)
我给你们截图看下
test_cases1 = unittest.TestLoader().loadTestsFromName('test1.myunittest2.Test3.test_minus')
import unittest from unittest import TestLoader from test1 import myunittest from test1.myunittest2 import Test3 as t3 if __name__ == '__main__': suite = unittest.TestSuite() loader = TestLoader() test_cases1 = unittest.TestLoader().loadTestsFromName('test1.myunittest2.Test3.test_minus') # 加载某个cese runner = unittest.TextTestRunner(verbosity=2) suite.addTests(test_cases1) runner.run(suite)
我截图给你们看下目录结构
在实际的项目中,有些案例咱们可能暂时不须要执行,若是有这样的问题,咱们该怎么办,unittest框架已经为咱们提供了解决方案
一、无条件跳过该案例,用该装饰器修饰要执行的案例,则该案例会被忽略不执行
@unittest.skip("do not exec")
@unittest.skip("do not exec") # 无条件跳过执行该案例 def test_add(self): self.assertEqual(3,func.add(1,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))
三、不知足某个条件才跳过案例
@unittest.skipUnless(4 < 3,"hahah")
@unittest.skipUnless(4 < 3,"hahah") # 不知足某个条件才跳过执行 def test_multi(self): self.assertEqual(4,func.multi(2,2))
四、咱们也能够在案例里面定义忽略执行这条案例
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()