单元测试框架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的主要内容,如果未能解决你的问题,请参考以下文章

Python单元测试框架:unittest

单元测试框架unittest

基于appium实现的线性代码引用unittest单元测试框架

Python 中 unittest 单元测试框架中需要知识点

python之单元测试框架—unittest(补充)

单元测试框架之unittest