終於等到十一,有時間寫博客了,準備利用十一這幾天的假期把這個系列的博客寫完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()