您好,登錄后才能下訂單哦!
終于等到十一,有時(shí)間寫博客了,準(zhǔn)備利用十一這幾天的假期把這個(gè)系列的博客寫完
該系列文章本人準(zhǔn)備寫三篇博客
第一篇:介紹python自動化測試框架unittest
第二篇:介紹django框架+request庫實(shí)現(xiàn)接口測試
第三篇:介紹利用Jenkins實(shí)現(xiàn)持續(xù)集成
今天進(jìn)入第一篇,unittest框架介紹
一、unittest簡述
unittest是python語言的單元測試框架,在python的官方文檔中,對unittest單元測試框架進(jìn)行了詳細(xì)的介紹,感興趣的讀者可以到https://www.python.org/doc
網(wǎng)站去了解;本篇博客重點(diǎn)介紹unittest單元測試框架在自動化測試中的應(yīng)用
unittest單元測試框架提供了創(chuàng)建測試用例,測試套件,和批量執(zhí)行測試用例的方法,在python安裝成功后,unittest單元測試框架可以直接導(dǎo)入使用,他屬于python的標(biāo)準(zhǔn)庫;作為單元測試的框架,unittest單元測試框架也是對程序的最小模塊進(jìn)行的一種敏捷化的測試。在自動化測試i中,我們雖然不需要做白盒測試,但是必須知道所使用語言的單元測試框架,這是因?yàn)楹竺嫖覀儨y試,就會遇到用例組織的問題,雖然函數(shù)式編程和面向?qū)ο缶幊烫峁┝藢Υa的重構(gòu),但是對于所編寫的每個(gè)測試用例,不可能編寫成一個(gè)函數(shù)來調(diào)用執(zhí)行;利用單元測試框架,可以創(chuàng)建一個(gè)類,該類繼承unittest的TestCase,這樣可以把每個(gè)TestCase看成是一個(gè)最小的單元,由測試套件組織起來,運(yùn)行時(shí)直接執(zhí)行即可,同時(shí)可引入測試報(bào)告。unittest各個(gè)組件的關(guān)系如果
TestCase------------------------------->TestFixture(測試固件)
|
|
|
TestSuite(測試套件)----------------------->TestRunner(測試執(zhí)行)-------------------->TestReport(測試報(bào)告)
# TestCase # 類,必須要繼承unittest.TestCase # 一個(gè)類class繼承 unittest.TestCase,就是一個(gè)測試用例。一個(gè)TestCase的實(shí)例就是一個(gè)測試用例,就是一個(gè)完整的測試流程。 # 包括測試前環(huán)境準(zhǔn)備setUp()|setUpClass()、執(zhí)行代碼run()、測試環(huán)境后的還原tearDown()|tearDownClass()。 # 繼承自unittest.TestCase的類中,測試方法的名稱要以test開頭。且只會執(zhí)行以test開頭定義的方法(測試用例)。
二、測試固件(TestFixture)
在unittest單元測試框架中,測試固件用于處理初始化的操作,例如,在對百度的搜索進(jìn)行測試前,首先需要打開瀏覽器并且進(jìn)入百度的首頁;測試結(jié)束后,需要關(guān)閉瀏覽器;測試固件提哦功能了兩種執(zhí)行形式,一種是每執(zhí)行一個(gè)測試用例,測試固件就會被執(zhí)行一次;另外一種就不管有多少個(gè)用例i,測試固件只會執(zhí)行一次
# 用于一個(gè)測試環(huán)境的準(zhǔn)備和銷毀還原。 # 當(dāng)測試用例每次執(zhí)行之前需要準(zhǔn)備測試環(huán)境,每次測試完成后還原測試環(huán)境,比如執(zhí)行前連接數(shù)據(jù)庫、打開瀏覽器等,執(zhí)行完成后需要還原數(shù)據(jù)庫、關(guān)閉瀏覽器等操作。 # 這時(shí)候就可以啟用testfixture。 # setUp():準(zhǔn)備環(huán)境,執(zhí)行每個(gè)測試用例的前置條件; # tearDown():環(huán)境還原,執(zhí)行每個(gè)測試用例的后置條件; # setUpClass():必須使用@classmethod裝飾器,所有case執(zhí)行的前置條件,只運(yùn)行一次; # tearDownClass():必須使用@classmethod裝飾器,所有case運(yùn)行完后只運(yùn)行一次;
1、測試固件每次均執(zhí)行
unittest單元測試框架提供了名為setUp的tearDown的測試固件。下面,我們通過編寫一個(gè)例子來看測試固件的執(zhí)行方式,測試代碼如下
import unittest class Test1(unittest.TestCase): # 測試固件之前置條件 def setUp(self): print("這是前置條件") # 測試固件之后置條件 def tearDown(self): print("這是后置條件") def test_case1(self): print("test_case1") def test_case2(self): print("test_case2") if __name__ == '__main__': unittest.main(verbosity=2)
執(zhí)行結(jié)果如下
他的執(zhí)行順序是先執(zhí)行setUp方法,在執(zhí)行具體的用例,最后執(zhí)行tearDown方法
2、測試固件只執(zhí)行一次
鉤子方法setUp和tearDown雖然經(jīng)常使用,但是在自動化測試中,一個(gè)系統(tǒng)的測試用例多達(dá)上千條,每次都執(zhí)行一次的setUp和tearDown方法會耗費(fèi)大量的性能,在unittest單元測試框架中還可以使用另外一種測試固件來解決這一問題,他就是setUpClass和tearDownClass方法,該測試固件方法是類方法,需要在方法上面加裝飾器@classmethod,使用該測試固件,不管有多少個(gè)用例,測試固件只執(zhí)行一次,具體代碼如下
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)
結(jié)果如下
3、兩種測試固件并存
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)
執(zhí)行結(jié)果如下
結(jié)果表明,先執(zhí)行被@classmethod裝飾器裝飾的測試固件,在執(zhí)行普通的測試固件
三、測試執(zhí)行
在以上事例中,可以看到測試用例的執(zhí)行是在主函數(shù)中,unittest調(diào)用的是main,代碼如下,TestProjram還是一個(gè)類,再來看該類的構(gòu)造函數(shù),代碼如下
main = TestProgram
TestProjram還是一個(gè)類,再來看該類的構(gòu)造函數(shù),代碼如下
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方法,可以方便的將測試模塊轉(zhuǎn)變?yōu)榭梢赃\(yùn)行的測試腳本。main使用unittest.TestLoader類來自動查找和加載模塊內(nèi)的測試用例,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)
在執(zhí)行測試用例時(shí)候,在main方法中加入了verbosity=2,代碼如下
if __name__ == '__main__': unittest.main(verbosity=2)
下面解釋一下verbosity部分,在verbosity中默認(rèn)是1。0代表執(zhí)行的測試總數(shù)和全局結(jié)果,2代表詳細(xì)的信息
四、測試套件,TestSuite
# TestSuite # 上述簡單的測試會產(chǎn)生兩個(gè)問題,可不可以控制test測試用例的執(zhí)行順序?若不想執(zhí)行某個(gè)測試用例,有沒有辦法可以跳過? # 對于執(zhí)行順序,默認(rèn)按照test的 A-Z、a-z的方法執(zhí)行。若要按自己編寫的用例的先后關(guān)系執(zhí)行,需要用到testSuite。 # 把多個(gè)測試用例集合起來,一起執(zhí)行,就是testSuite。testsuite還可以包含testsuite。 # 一般通過addTest()或者addTests()向suite中添加。case的執(zhí)行順序與添加到Suite中的順序是一致的。
1、直接執(zhí)行案例
我們在func.py這個(gè)文件中定義加減乘除4個(gè)測試函數(shù)
#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)
執(zhí)行結(jié)果如下
2、添加案例到測試套件中
上述簡單的測試會產(chǎn)生兩個(gè)問題,可不可以控制test測試用例的執(zhí)行順序?若不想執(zhí)行某個(gè)測試用例,有沒有辦法可以跳過?對于執(zhí)行順序,默認(rèn)按照test的 A-Z、a-z的方法執(zhí)行。若要按自己編寫的用例的先后關(guān)系執(zhí)行,需要用到testSuite。把多個(gè)測試用例集合起來,一起執(zhí)行,就是testSuite。testsuite還可以包含testsuite。一般通過addTest()或者addTests()向suite中添加。case的執(zhí)行順序與添加到Suite中的順序是一致的。
如果用到測試套件TestSuite,則需要先寫好測試代碼,但是先不要執(zhí)行
我們同樣在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__': # 輸出信息到控制臺 # 實(shí)例化一個(gè)TestSuite類 suite = unittest.TestSuite() # 把需要執(zhí)行的案例放在一個(gè)list中 tests = [Test3("test_add"), Test3("test_minus"), Test3("test_multi"), Test3("test_divide")] # 把案例添加到實(shí)例化好的測試套件中 suite.addTests(tests) # t = [t3("test_add"), t3("test_minus"), t3("test_multi"), t3("test_divide")] # suite.addTests(tests) # 實(shí)例化一個(gè)參數(shù)執(zhí)行類 runner = unittest.TextTestRunner(verbosity=2) # 測試執(zhí)行類的實(shí)例執(zhí)行測試套件 runner.run(suite)
以上的案例我們是添加一個(gè)文件的測試案例,我們同樣可以添加多個(gè)文件中的案例到一個(gè)測試套件中,然后執(zhí)行這個(gè)測試套件即可
import unittest from test1.myunittest import Test3 from test1.myunittest2 import Test3 as t3 if __name__ == '__main__': # 輸出信息到控制臺 # 實(shí)例化一個(gè)TestSuite類 suite = unittest.TestSuite() # 把需要執(zhí)行的案例放在一個(gè)list中 tests = [Test3("test_add"), Test3("test_minus"), Test3("test_multi"), Test3("test_divide")] # 把案例添加到實(shí)例化好的測試套件中 suite.addTests(tests) # 添加另外一個(gè)文件中的測試案例到測試套件中 t = [t3("test_add"), t3("test_minus"), t3("test_multi"), t3("test_divide")] suite.addTests(t) # 實(shí)例化一個(gè)參數(shù)執(zhí)行類 runner = unittest.TextTestRunner(verbosity=2) # 測試執(zhí)行類的實(shí)例執(zhí)行測試套件 runner.run(suite)
上面的執(zhí)行方式是輸出結(jié)果到控制臺,我們也可以輸出結(jié)果到文件中
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)
3、直接添加測試類到測試套件中
案例一個(gè)一個(gè)添加還是比較麻煩,我們可以直接添加一個(gè)測試類到測試套件中
利用下面的方法加載一個(gè)測試類
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) # 參數(shù)是一個(gè)類,而這個(gè)類必須是unittest.TestCase的子類或者孫類 suite.addTests(test_cases1) runner = unittest.TextTestRunner(verbosity=2) runner.run(suite)
4、直接加載一個(gè)模塊到測試套件中,如果這個(gè)模塊中有多個(gè)類,則會把所有的類的測試案例加載到測試套件中
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) # 參數(shù)是一個(gè)模塊,會把這個(gè)模塊里的所有case加載進(jìn)來 suite.addTests(test_cases1) runner = unittest.TextTestRunner(verbosity=2) runner.run(suite)
我給大家截圖看下
5、通過案例名稱添加案例到測試套件中
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') # 加載某個(gè)cese runner = unittest.TextTestRunner(verbosity=2) suite.addTests(test_cases1) runner.run(suite)
我截圖給大家看下目錄結(jié)構(gòu)
五、忽略執(zhí)行案例
在實(shí)際的項(xiàng)目中,有些案例我們可能暫時(shí)不需要執(zhí)行,如果有這樣的問題,我們該怎么辦,unittest框架已經(jīng)為我們提供了解決方案
1、無條件跳過該案例,用該裝飾器修飾要執(zhí)行的案例,則該案例會被忽略不執(zhí)行
@unittest.skip("do not exec")
@unittest.skip("do not exec") # 無條件跳過執(zhí)行該案例 def test_add(self): self.assertEqual(3,func.add(1,2))
2、滿足某個(gè)條件才跳過該案例
@unittest.skipIf(4 > 3,"2 > 3 do not exec")
@unittest.skipIf(4 > 3,"2 > 3 do not exec") # 滿足某個(gè)條件才跳過執(zhí)行 def test_minus(self): self.assertEqual(4,func.minus(5,1))
3、不滿足某個(gè)條件才跳過案例
@unittest.skipUnless(4 < 3,"hahah")
@unittest.skipUnless(4 < 3,"hahah") # 不滿足某個(gè)條件才跳過執(zhí)行 def test_multi(self): self.assertEqual(4,func.multi(2,2))
4、我們也可以在案例里面定義忽略執(zhí)行這條案例
def test_divide(self): self.skipTest("wydd") self.assertEqual(10,func.divide(100,10))
六、斷言
斷言就是判斷實(shí)際測試結(jié)果與預(yù)期結(jié)果是否一致,一致則測試通過,否則失敗。因此,在自動化測試中,無斷言的測試用例是無效的,這是因?yàn)楫?dāng)一個(gè)功能自動化已經(jīng)全部實(shí)現(xiàn),在每次版本迭代中執(zhí)行測試用例時(shí),執(zhí)行的結(jié)果必須是權(quán)威的,也就是說自動化測試用例執(zhí)行結(jié)果應(yīng)該無功能性或者邏輯性問題,在自動化測試中最忌諱的就是自動化測試的用例雖然是通過的,但是被測試的功能卻是存在問題的,自動化測試用例經(jīng)常應(yīng)用在回歸測試中,發(fā)現(xiàn)的問題不是特別多,如果測試結(jié)果存在功能上的問題,則投入了人力去做的自動化參數(shù)就沒有多大的意義了,所以每一個(gè)測試用例必須要有斷言;在測試的結(jié)果中只有兩種可能,一種是執(zhí)行通過,另外一種是執(zhí)行失敗,也就是功能存在問題,在TestCase類中提供了assert方法來檢查和報(bào)告失敗,常用的方法如下
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() # 判斷在某個(gè)范圍內(nèi) self.assertNotIn() # 判斷是否不在某個(gè)范圍內(nèi) self.assertIsInstance() # 判斷是否為某個(gè)類的實(shí)例 self.assertNotIsInstance()
到此這篇關(guān)于python自動化測試三部曲之unittest框架的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)python unittest框架內(nèi)容請搜索億速云以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持億速云!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。