单元测试框架Unittest
Posted zeke-python-road
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单元测试框架Unittest相关的知识,希望对你有一定的参考价值。
Unittest官方 4个重要概念:
Test fixture(测试固件):初始化、清除
Test case(测试用例),test suite(测试套件),test runner(测试运行器)
两种单元测试加载方法:
1.unittest.main()
2.将所有test case 添加到test suit中,然后一次性加载
知识点:
1.测试类要继承unittest.TestCase类
2.每个用例方法 test开头(self)
3.setUp和tearDown方法在每个用例执行前后都会执行
4.unittest.main()执行同一模块内unittest子类所有方法
实例
被测试模块 Calc.py
后续部分用例会引用此模块
1 # coding=utf-8 2 class Calc(object): 3 def add(self, x, y, *d): 4 # 加法计算 5 result = x + y 6 for i in d: 7 result += i 8 return result 9 10 def sub(self, x, y, *d): 11 # 减法计算 12 result = x - y 13 for i in d: 14 result -= i 15 return result 16 17 @classmethod 18 def mul(cls, x, y, *d): 19 # 乘法计算 20 result = x * y 21 for i in d: 22 result *= i 23 return result 24 25 @staticmethod 26 def div(x, y, *d): 27 # 除法计算 28 if y != 0: 29 result = x / y 30 else: 31 return -1 32 for i in d: 33 if i != 0: 34 result /= i 35 else: 36 return -1 37 return result
例子1 用例初始化清除setUp、tearDown
1 #encoding=utf-8 2 import unittest 3 import random 4 class TestSequenceFunctions(unittest.TestCase): 5 def setUp(self): 6 # 初始化一个递增序列 7 self.seq = range(10) 8 print "setup completed!" 9 10 def test_run(self): 11 # 从序列seq中随机选取一个元素 12 element = random.choice(self.seq) 13 # 验证随机元素确实属于列表中 14 self.assertTrue(element in self.seq) 15 16 def test_sth(self): 17 assert 1==1 18 19 def tearDown(self): 20 print "tearDown completed" 21 22 class TestDictValueFormatFunctions(unittest.TestCase): 23 def setUp(self): 24 self.seq = range(10) 25 26 def test_shuffle(self): 27 # 随机打乱原seq的顺序 28 random.shuffle(self.seq) 29 self.seq.sort() 30 self.assertEqual(self.seq, range(10)) 31 # 验证执行函数时抛出了TypeError异常 32 self.assertRaises(TypeError, random.shuffle, (1, 2, 3)) 33 34 if __name__ == ‘__main__‘: 35 unittest.main()
例子2 类初始化清除setUpClass(cls)、tearDownClass(cls)
类里所有用例执行前后仅执行一次
1 #encoding=utf-8 2 import unittest 3 4 # 被测试类 5 class myclass(object): 6 @classmethod 7 def sum(self, a, b): 8 return a + b #将两个传入参数进行相加操作 9 10 @classmethod 11 def sub(self, a, b): 12 return a - b #将两个传入参数进行相减操作 13 14 15 class mytest(unittest.TestCase): 16 @classmethod 17 def setUpClass(cls): 18 "初始化类固件" 19 print "----setUpClass" 20 21 @classmethod 22 def tearDownClass(cls): 23 "重构类固件" 24 print "----tearDownClass" 25 26 # 初始化工作 27 def setUp(self): 28 self.a = 3 29 self.b = 1 30 print "--setUp" 31 32 # 退出清理工作 33 def tearDown(self): 34 print "--tearDown" 35 36 # 具体的测试用例,一定要以test开头 37 def testsum(self): 38 # 断言两数之和的结果是否是4 39 self.assertEqual(myclass.sum(self.a, self.b), 4, ‘test sum fail‘) 40 41 def testsub(self): 42 # 断言两数之差的结果是否是2 43 self.assertEqual(myclass.sub(self.a, self.b), 2, ‘test sub fail‘) 44 45 46 if __name__ == ‘__main__‘: 47 unittest.main() # 启动单元测试
例子3 按特定顺序执行用例
unittest框架用例执行顺序是按照用例名字符的ASCII码顺序
用例数小于10:可将用例名命名为test加0-9, 按数字由小到大顺序执行
用例数大于10:可将用例名字首字母开始命名 a-Z按顺序执行,如第一字母相同,则第二字母再按a-Z顺序排序,以此类推
suit.addTest(class(‘testcase’)) 按此方式添加的用例会按添加顺序执行
此代码需要文本开头的被测试模块Calc.py
1 #encoding=utf-8 2 import unittest 3 from Calc import Calc 4 5 class MyTest(unittest.TestCase): 6 7 @classmethod 8 def setUpClass(self): 9 print u"单元测试前,创建Calc类的实例" 10 self.c = Calc() 11 12 # 具体的测试用例,一定要以test开头,执行顺序按照字母顺序开头 13 def test_0add(self): 14 print "run add()" 15 self.assertEqual(self.c.add(1, 2, 12), 15, ‘test add fail‘) 16 17 def test_1sub(self): 18 print "run sub()" 19 self.assertEqual(self.c.sub(2, 1, 3), -2, ‘test sub fail‘) 20 21 def test_2mul(self): 22 print "run mul()" 23 self.assertEqual(Calc.mul(2, 3, 5), 30, ‘test mul fail‘) 24 25 def test_3div(self): 26 print "run div()" 27 self.assertEqual(Calc.div(8, 2, 4), 1, ‘test div fail‘) 28 29 if __name__ == ‘__main__‘: 30 unittest.main()# 启动单元测试 31 32 33 if __name__ == ‘__main__‘: 34 suite = unittest.TestSuite() 35 suite.addTest(MyTest(‘test_2mu’)) 36 suite.addTest(MyTest(‘test_1sub’)) 37 runner = unittest.TextTestRunner() 38 runner.run(suite)
例子4 忽略测试方法,不想执行的用例,可跳过
unittet可以分无条件忽略和有条件忽略,通过装饰器实现
跳过用例时打印括号内后面参数的内容(跳过原因)
@unittest.skip(reason) 装饰器:无条件跳过装饰的测试,并说明跳过测试的原因。
@unittest.skipIf(a > 5, "condition is not satisfied!") 条件为真时,跳过装饰的测试,并说明跳过测试的原因。
@unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")条件为假时,跳过装饰的测试,并说明跳过测试的原因。
@unittest.expectedFailure(): expectedFailure()测试标记为失败。
1 # coding=utf-8 2 import random 3 import unittest 4 import sys 5 6 class TestSequenceFunctions(unittest.TestCase): 7 a = 1 8 9 def setUp(self): 10 self.seq = list(range(10)) 11 12 @unittest.skip("skipping") # 无条件忽略该测试方法 13 def test_shuffle(self): 14 random.shuffle(self.seq) 15 self.seq.sort() 16 self.assertEqual(self.seq, list(range(10))) 17 self.assertRaises(TypeError, random.shuffle, (1, 2, 3)) 18 19 # 如果变量a > 5,则忽略该测试方法 20 @unittest.skipIf(a > 5, "condition is not satisfied!") 21 def test_choice(self): 22 element = random.choice(self.seq) 23 self.assertTrue(element in self.seq) 24 25 # 除非执行测试用例的平台是Linux平台,否则忽略该测试方法是windows 26 @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") 27 def test_sample(self): 28 with self.assertRaises(ValueError): 29 random.sample(self.seq, 20) 30 for element in random.sample(self.seq, 5): 31 self.assertTrue(element in self.seq) 32 33 34 if __name__ == ‘__main__‘: 35 # unittest.main() 36 Case = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions) 37 suite = unittest.TestSuite(Case) 38 unittest.TextTestRunner(verbosity = 2).run(suite)
例子5 测试集合,批量执行测试用例
根据给定的测试类,获取其中的所有以“test”开头的测试方法,并返回一个测试套件
suite1=unittest.TestLoader().loadTestsFromTestCase(Class)
将多个测试类加载到测试套件中
suite = unittest.TestSuite([suite2, suite1,suite3])
通过调整suit2和suite1的顺序,可以设定执行顺序
设置verbosity = 2,可以打印出更详细的执行信息
unittest.TextTestRunner(verbosity = 2).run(suite)
方式1:模块中直接调用unittest.main()
此代码需要文本开头的被测试模块Calc.py
此代码模块名:TestCalc.py(下一段代码会用到)
1 #encoding=utf-8 2 import unittest 3 import random 4 from Calc import Calc 5 6 class TestCalcFunctions(unittest.TestCase): 7 def setUp(self): 8 self.c=Calc() 9 print "setup completed!" 10 11 def test_sum(self): 12 self.assertTrue(self.c.add(1,2,3,4)==10) 13 14 def test_sub(self): 15 self.assertTrue(self.c.sub(100,20,30,40)==10) 16 17 def test_mul(self): 18 self.assertTrue(self.c.mul(1,2,3,40)==240) 19 20 def test_div(self): 21 self.assertTrue(self.c.div(100,10,2)==5) 22 23 def tearDown(self): 24 25 print "test completed!" 26 27 28 def tearDown(self): 29 print "tearDown completed" 30 31 if __name__ == ‘__main__‘: 32 unittest.main()
方式2:测试用例类加载到一个套件中
1 #encoding=utf-8 2 import random 3 import unittest 4 from TestCalc import TestCalcFunctions 5 class TestSequenceFunctions(unittest.TestCase): 6 def setUp(self): 7 self.seq = range(10) 8 9 def tearDown(self): 10 pass 11 12 def test_choice(self): 13 # 从序列seq中随机选取一个元素 14 element = random.choice(self.seq) 15 # 验证随机元素确实属于列表中 16 self.assertTrue(element in self.seq) 17 18 def test_sample(self): 19 # 验证执行的语句是否抛出了异常 20 with self.assertRaises(ValueError): 21 random.sample(self.seq, 20) 22 for element in random.sample(self.seq, 5): 23 self.assertTrue(element in self.seq) 24 25 26 class TestDictValueFormatFunctions(unittest.TestCase): 27 def setUp(self): 28 self.seq = range(10) 29 30 def tearDown(self): 31 pass 32 33 def test_shuffle(self): 34 # 随机打乱原seq的顺序 35 random.shuffle(self.seq) 36 self.seq.sort() 37 self.assertEqual(self.seq, range(10)) 38 # 验证执行函数时抛出了TypeError异常 39 self.assertRaises(TypeError, random.shuffle, (1, 2, 3)) 40 41 if __name__ == ‘__main__‘: 42 # 根据给定的测试类,获取其中的所有以“test”开头的测试方法,并返回一个测试套件 43 suite1=unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions) 44 suite2= unittest.TestLoader().loadTestsFromTestCase(TestDictValueFormatFunctions) 45 suite3 = unittest.TestLoader().loadTestsFromTestCase(TestCalcFunctions) 46 # 将多个测试类加载到测试套件中 47 suite = unittest.TestSuite([suite2, suite1,suite3]) #通过调整suit2和suite1的顺序,可以设定执行顺序 48 # 设置verbosity = 2,可以打印出更详细的执行信息 49 unittest.TextTestRunner(verbosity = 2).run(suite)
例子6 断言
self.assertRaises(TypeError,callable,argvs)验证执行函数时抛出了TypeError异常
执行用例时出现异常,该用例会打印一个E;断言失败则会打印F,代表Fail
1 #encoding=utf-8 2 import unittest 3 import random 4 5 # 被测试类 6 class MyClass(object): 7 @classmethod 8 def sum(self, a, b): 9 return a + b 10 11 @classmethod 12 def div(self, a, b): 13 return a / b 14 15 @classmethod 16 def retrun_None(self): 17 return None 18 19 # 单元测试类 20 class MyTest(unittest.TestCase): 21 22 # assertEqual()方法实例 23 def test_assertEqual(self): 24 # 断言两数之和的结果 25 try: 26 a, b = 1, 2 27 sum = 3 28 self.assertEqual(a + b, sum, ‘断言失败,%s + %s != %s‘ %(a, b, sum)) 29 except AssertionError, e: 30 print e 31 32 # assertNotEqual()方法实例 33 def test_assertNotEqual(self): 34 # 断言两数之差的结果 35 try: 36 a, b = 5, 2 37 res = 1 38 self.assertNotEqual(a - b, res, ‘断言失败,%s - %s != %s‘ %(a, b, res)) 39 except AssertionError, e: 40 print e 41 42 # assertTrue()方法实例 43 def test_assertTrue(self): 44 # 断言表达式的为真 45 try: 46 self.assertTrue(1 == 1, "表达式为假") 47 except AssertionError, e: 48 print e 49 50 # assertFalse()方法实例 51 def test_assertFalse(self): 52 # 断言表达式为假 53 try: 54 self.assertFalse(3 == 2, "表达式为真") 55 except AssertionError, e: 56 print e 57 58 # assertIs()方法实例 59 def test_assertIs(self): 60 # 断言两变量类型属于同一对象 61 try: 62 a = 12 63 b = a 64 self.assertIs(a, b, "%s与%s不属于同一对象" %(a, b)) 65 except AssertionError, e: 66 print e 67 68 # test_assertIsNot()方法实例 69 def test_assertIsNot(self): 70 # 断言两变量类型不属于同一对象 71 try: 72 a = 12 73 b = "test" 74 self.assertIsNot(a, b, "%s与%s属于同一对象" %(a, b)) 75 except AssertionError, e: 76 print e 77 78 # assertIsNone()方法实例 79 def test_assertIsNone(self): 80 # 断言表达式结果为None 81 try: 82 result = MyClass.retrun_None() 83 self.assertIsNone(result, "not is None") 84 except AssertionError, e: 85 print e 86 87 # assertIsNotNone()方法实例 88 def test_assertIsNotNone(self): 89 # 断言表达式结果不为None 90 try: 91 result = MyClass.sum(2, 5) 92 self.assertIsNotNone(result, "is None") 93 except AssertionError, e: 94 print e 95 96 # assertIn()方法实例 97 def test_assertIn(self): 98 # 断言对象A是否包含在对象B中 99 try: 100 strA = "this is a test" 101 strB = "is" 102 self.assertIn(strB, strA, "%s不包含在%s中" %(strB, strA)) 103 except AssertionError, e: 104 print e 105 106 # assertNotIn()方法实例 107 def test_assertNotIn(self): 108 # 断言对象A不包含在对象B中 109 try: 110 strA = "this is a test" 111 strB = "Selenium" 112 self.assertNotIn(strB, strA, "%s包含在%s中" %(strB, strA)) 113 except AssertionError, e: 114 print e 115 116 # assertIsInstance()方法实例 117 def test_assertIsInstance(self): 118 # 测试对象A的类型是否值指定的类型 119 try: 120 x = MyClass 121 y = object 122 self.assertIsInstance(x, y, "%s的类型不是%s".decode("utf-8") %(x, y)) 123 except AssertionError, e: 124 print e 125 126 # assertNotIsInstance()方法实例 127 def test_assertNotIsInstance(self): 128 # 测试对象A的类型不是指定的类型 129 try: 130 a = 123 131 b = str 132 self.assertNotIsInstance(a, b, "%s的类型是%s" %(a, b)) 133 except AssertionError, e: 134 print e 135 136 # assertRaises()方法实例 137 def test_assertRaises(self): 138 # 测试抛出的指定的异常类型 139 # assertRaises(exception) 140 with self.assertRaises(ValueError) as cm: 141 random.sample([1,2,3,4,5], "j") 142 # 打印详细的异常信息 143 #print "===", cm.exception 144 145 # assertRaises(exception, callable, *args, **kwds) 146 try: 147 self.assertRaises(ZeroDivisionError, MyClass.div, 3, 0) 148 except ZeroDivisionError, e: 149 print e 150 151 # assertRaisesRegexp()方法实例 152 def test_assertRaisesRegexp(self): 153 # 测试抛出的指定异常类型,并用正则表达式具体验证 154 # assertRaisesRegexp(exception, regexp) 155 with self.assertRaisesRegexp(ValueError, ‘literal‘) as ar: 156 int("xyz") 157 # 打印详细的异常信息 158 #print ar.exception 159 # 打印正则表达式 160 #print "re:",ar.expected_regexp 161 162 # assertRaisesRegexp(exception, regexp, callable, *args, **kwds) 163 try: 164 self.assertRaisesRegexp(ValueError, "invalid literal for.*XYZ‘$", int, ‘XYZ‘) 165 except AssertionError, e: 166 print e 167 168 169 if __name__ == ‘__main__‘: 170 # 执行单元测试 171 unittest.main()
例子7 生成报告
把HTMLTestRunner.py和执行程序文件放置同一目录
1 # coding=utf-8 2 import unittest 3 import htmlTestRunner 4 import math 5 6 class Calc(object): 7 8 def add(self, x, y, *d): 9 # 加法计算 10 result = x + y 11 for i in d: 12 result += i 13 return result 14 15 def sub(self, x, y, *d): 16 # 减法计算 17 result = x - y 18 for i in d: 19 result -= i 20 return result 21 22 class SuiteTestCalc(unittest.TestCase): 23 def setUp(self): 24 self.c = Calc() 25 26 @unittest.skip("skipping") 27 def test_Sub(self): 28 print "sub" 29 self.assertEqual(self.c.sub(100, 34, 6), 61, u‘求差结果错误!‘) 30 31 def testAdd(self): 32 print "add" 33 self.assertEqual(self.c.add(1, 32, 56), 89, u‘求和结果错误!‘) 34 35 36 class SuiteTestPow(unittest.TestCase): 37 def setUp(self): 38 self.seq = range(10) 39 40 # @unittest.skipIf() 41 def test_Pow(self): 42 print "Pow" 43 self.assertEqual(pow(6, 3), 2161, u‘求幂结果错误!‘) 44 45 def test_hasattr(self): 46 print "hasattr" 47 # 检测math模块是否存在pow属性 48 self.assertTrue(hasattr(math, ‘pow1‘), u"检测的属性不存在!") 49 50 if __name__ == "__main__": 51 suite1 = unittest.TestLoader().loadTestsFromTestCase(SuiteTestCalc) 52 suite2 = unittest.TestLoader().loadTestsFromTestCase(SuiteTestPow) 53 suite = unittest.TestSuite([suite1, suite2]) 54 #unittest.TextTestRunner(verbosity=2).run(suite) 55 filename = "test.html" # 定义个报告存放路径,支持相对路径。 56 # 以二进制方式打开文件,准备写 57 fp = file(filename, ‘wb‘) 58 # 使用HTMLTestRunner配置参数,输出报告路径、报告标题、描述,均可以配 59 runner = HTMLTestRunner.HTMLTestRunner(stream = fp, 60 title = ‘Report_title‘, description = ‘Report_description‘) 61 # 运行测试集合 62 runner.run(suite)
例子8 批量执行多个模块生成报告
1程序文件模式
即将测试发现代码编写在测试脚本中,然后直接执行脚本文件即可,具体由
TestLoader().discover(dir)实现,dir为被测试模块目录
模块名需test开头,测试方法也要test开头,类名不需要test开头
1 #coding=utf-8 2 import unittest 3 import HTMLTestRunner 4 5 if __name__ == ‘__main__‘ : 6 # 加载当前目录下所有有效的测试模块(以test开头的文件),“.”表示当前目录 7 testSuite = unittest.TestLoader().discover(‘.‘) 8 filename = "test.html" # 定义个报告存放路径,支持相对路径。 9 # 以二进制方式打开文件,准备写 10 fp = file(filename, ‘wb‘) 11 # 使用HTMLTestRunner配置参数,输出报告路径、报告标题、描述,均可以配 12 runner = HTMLTestRunner.HTMLTestRunner(stream = fp, 13 title = ‘Report_title‘, description = ‘Report_description‘) 14 # 运行测试集合 15 runner.run(testSuite)
2命令行模式
unittest支持简单的test discovery. 命令行传入discovery后,框架会自动在当前目录搜索要测试的案例并执行.搜索目录必须是包或者模块.基本使用如下:
python -m unittest discover
子选项如下:
-v, –verbose
python -m unittest discover -v
输出信息的详细级别
-s, –start-directory directory
python -m unittest discover -s other_dir
开始搜索目录 (默认为当前目录)
-p, –pattern pattern
python -m unittest discover -p ‘YW*.py’
匹配的文件名 (默认为test*.py)
-t, –top-level-directory directory
python -m unittest discover -t
g:
搜索的顶层目录 (默认为start directory)
例子9 webdriver和unittest结合
1 #encoding=utf-8 2 import unittest 3 from selenium import webdriver 4 import time 5 6 class WebTest(unittest.TestCase): 7 def setUp(self): 8 # 启动Firefox浏览器 9 self.driver=webdriver.Firefox(executable_path = "d:\drivergeckodriver") 10 11 def testSoGou(self): 12 # 访问搜狗首页 13 self.driver.get("http://sogou.com") 14 # 清空搜索输入框默认内容 15 self.driver.find_element_by_id("query").clear() 16 # 在搜索输入框中输入“自动化测试” 17 self.driver.find_element_by_id("query").send_keys(u"自动化测试") 18 # 单击“搜索”按钮 19 self.driver.find_element_by_id("stb").click() 20 # 等待3秒 21 time.sleep(3) 22 assert u"web自动化" in self.driver.page_source, u"页面中不存在要寻找的关键字!".encode("gbk") 23 24 25 def testBing(self): 26 # 访问搜狗首页 27 self.driver.get("http://cn.bing.com") 28 # 清空搜索输入框默认内容 29 self.driver.find_element_by_id("sb_form_q").clear() 30 # 在搜索输入框中输入“自动化测试” 31 self.driver.find_element_by_id("sb_form_q").send_keys(u"自动化测试") 32 # 单击“搜索”按钮 33 self.driver.find_element_by_id("sb_form_go").click() 34 # 等待3秒 35 time.sleep(3) 36 assert u"web自动化" in self.driver.page_source, u"页面中不存在要寻找的关键字!".encode("gbk") 37 38 39 def tearDown(self): 40 # 退出浏览器 41 self.driver.quit() 42 43 if __name__ == ‘__main__‘: 44 unittest.main()
例子10 补充:命令行模式执行测试用例
1. 运行整个测试模块
python -m unittest test_module1 test_module2……
2. 执行测试模块中某个测试类
python -m unittest test_module.TestClass
3. 执行测试模块中某个测试类下的测试方法
python -m unittest test_module.TestClass.test_method
练习
写一个读文件的类,里面有个方法可以读取文件的全部内容。
写个单元测试,断言文件中包含关键字"well done!"
1 #被测试模块:readcont.py 2 3 #coding=utf-8 4 import unittest 5 import os.path 6 7 class TestReadContent(): 8 @staticmethod 9 def read_content(filepath): 10 if os.path.exists(filepath): 11 with open(filepath) as fp: 12 return fp.read() 13 else: 14 return ‘‘ 15 if __name__==‘__main__‘: 16 fcon = TestReadContent.read_content(‘g:/a.txt‘) 17 print fcon 18 19 #单元测试模块: 20 21 #coding= utf-8 22 import unittest 23 from readcont import TestReadContent 24 25 class TestRead(unittest.TestCase): 26 def test_run(self): 27 content = TestReadContent.read_content(‘g:/a.txt‘) 28 self.assertTrue(‘well done!‘ in content ) 29 30 if __name__==‘__main__‘: 31 unittest.main()
以上是关于单元测试框架Unittest的主要内容,如果未能解决你的问题,请参考以下文章
基于appium实现的线性代码引用unittest单元测试框架