單元測試框架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:\\driver\geckodriver")
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()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM