unittest测试框架_python接口自动化测试 - 4.unittest单元测试框架学习
簡(jiǎn)介
unittest就是python的一個(gè)單元測(cè)試框架,unittest非常適合做自動(dòng)化測(cè)試。
官方源碼栗子:
import unittestclass IntegerArithmeticTestCase(unittest.TestCase):def testAdd(self): # test method names begin with 'test'self.assertEqual((1 + 2), 3)self.assertEqual(0 + 1, 1)def testMultiply(self):self.assertEqual((0 * 10), 0)self.assertEqual((5 * 8), 40)if __name__ == '__main__':unittest.main()解析下這個(gè)源碼:
1. 引入unittest包3. class定義一個(gè)測(cè)試類,并繼承unittest.TestCase這個(gè)類
4 - 9. 定義兩個(gè)測(cè)試case的名稱,分別是testAdd和testMultiply, 然后增加了斷言。assertEqual()
Note: 第四行中 “test method names begin with 'test'” 我們定義測(cè)試case的名稱必須以test開頭
11 - 12. 這個(gè)程序的主函數(shù)
運(yùn)行的結(jié)果:
.. ---------------------------------------------------------------------- Ran 2 tests in 0.001sOKunittest中一些重要的概念
靜態(tài)框架圖如下:
TestCase(測(cè)試用例): 所有測(cè)試用例的基類,它是軟件 測(cè)試中最基本的組成單元。
一個(gè)test case就是一個(gè)測(cè)試用例,是一個(gè)完整的測(cè)試流程,包括測(cè)試前環(huán)境的搭建setUp,執(zhí)行測(cè)試代碼(run),以及測(cè)試后環(huán)境的還原(tearDown)。測(cè)試用例是一個(gè)完整的測(cè)試單元,可以對(duì)某一問題進(jìn)行驗(yàn)證。
TestSuite(測(cè)試套件):,多個(gè)測(cè)試用例test case集合就是TestSuite,TestSuite可以嵌套TestSuite
TestLoder:是用來(lái)加載 TestCase到TestSuite中,其中有幾個(gè)loadTestsFrom_()方法,就是從各個(gè)地方尋找TestCase,創(chuàng)建他們的實(shí)例,然后add到TestSuite中,再返回一個(gè)TestSuite實(shí)例
TextTestRunner:是來(lái)執(zhí)行測(cè)試用例的,其中的run(test)會(huì)執(zhí)行TestSuite/TestCase中的run(result)方法。
TextTestResult:測(cè)試結(jié)果會(huì)保存到TextTestResult實(shí)例中,包括運(yùn)行了多少用例,成功與失敗多少等信息
TestFixture:又叫測(cè)試腳手,測(cè)試代碼的運(yùn)行環(huán)境,指測(cè)試準(zhǔn)備前和執(zhí)行后要做的工作,包括setUp和tearDown方法。總結(jié)就是:對(duì)一個(gè)測(cè)試用例環(huán)境的搭建和銷毀。如何銷毀呢?就是通過覆蓋TestCase的setUp()和tearDown()方法來(lái)實(shí)現(xiàn),tearDown()的過程很重要,為后面的case保證了一個(gè)干凈的測(cè)試環(huán)境。
一個(gè)class繼承了unittest.TestCase,便是一個(gè)測(cè)試用例,但如果其中有多個(gè)以 test 開頭的方法,那么每有一個(gè)這樣的方法,在load的時(shí)候便會(huì)生成一個(gè)TestCase實(shí)例,如:一個(gè)class中有四個(gè)test_xxx方法,最后在load到suite中時(shí)也有四個(gè)測(cè)試用例。
unittest工作原理
我們可以簡(jiǎn)化一下就是:
先寫好TestCase然后由TestLoader加載TestCase到TestSuite,然后由TextTestRunner來(lái)運(yùn)行TestSuite,運(yùn)行的結(jié)果保存在TextTestResult中,整個(gè)過程集成在unittest.main模塊中,main會(huì)調(diào)用TextTestRunner中的run來(lái)執(zhí)行,或者我們可以直接通過TextTestRunner來(lái)執(zhí)行用例。在Runner運(yùn)行的時(shí)候,我們的測(cè)試結(jié)果會(huì)被輸出到控制臺(tái),可以清晰的看到,我們還可以輸出到文件,運(yùn)用HTMLTestRunner生成一個(gè)漂亮的報(bào)告。
unittest case調(diào)用順序
其中我們一個(gè)TestCase調(diào)用的順序如下圖所示:
舉個(gè)栗子
這是我項(xiàng)目中 uiautomator2框架 + python unittest 寫的手機(jī)自動(dòng)化測(cè)試用例中的一小部分
import time import unittest import uiautomator2 as u2from NormativeExamination import commonclass GoogleSecurityCheck(unittest.TestCase):@classmethoddef setUpClass(cls):cls.d = u2.connect('04030148AO000175')cls.d.make_toast('測(cè)試開始', 3)@classmethoddef tearDownClass(cls):cls.d.make_toast('測(cè)試結(jié)束', 3)def setUp(self):self.d.info.get("screenOn")self.d.screen_on()def tearDown(self):self.d.press('Home')time.sleep(1)def test_persistent(self):u'''persistent進(jìn)程內(nèi)存占用'''persistent, code = self.d.shell("dumpsys meminfo | grep persistent")result = persistent.split("K:")[0].replace(",", "")self.assertLessEqual(int(result), 92160, 'persistent小于90M')def test_bluetooth(self):u'''測(cè)試藍(lán)牙狀態(tài), 藍(lán)牙必須默認(rèn)關(guān)閉'''common.open_settings_menu(self.d, "Bluetooth")bluetooth_off = self.d(resourceId="com.android.settings:id/switch_widget", text=u"OFF")self.assertTrue(bluetooth_off, "藍(lán)牙默認(rèn)是關(guān)閉的") if __name__ == '__main__':unittest.main()上面這個(gè)栗子中,還有幾個(gè)額外的知識(shí):
setup就是前置條件,tearDown就是后置條件,在我們執(zhí)行完case之后,tearDown最好要寫,做數(shù)據(jù)的還原,清理測(cè)試環(huán)境,比如退出瀏覽器、返回到手機(jī)的Home頁(yè)面,保證后面的case不會(huì)執(zhí)行失敗。
這里有個(gè)坑,就是比如我們做測(cè)試打開百度頁(yè)面的操作,每次執(zhí)行用例就重新打開一次,這樣就非常的浪費(fèi)時(shí)間,我的本意是打開了百度的頁(yè)面,我想做完所有的操作,然后再去關(guān)閉百度頁(yè)面,這個(gè)時(shí)候就用到了裝飾器(@classmethod)
- 裝飾器
用setUp與setUpClass區(qū)別
setup():每個(gè)測(cè)試case運(yùn)行前運(yùn)行
teardown():每個(gè)測(cè)試case運(yùn)行完后執(zhí)行
setUpClass():必須使用@classmethod 裝飾器,所有case運(yùn)行前只運(yùn)行一次
tearDownClass():必須使用@classmethod裝飾器,所有case運(yùn)行完后只運(yùn)行一次
- @是修飾符,classmethod是python里的類方法
Note: 在寫測(cè)試用例的時(shí)候,一定要用test開頭,假如不用test開頭,你會(huì)發(fā)現(xiàn)程序識(shí)別不了。這個(gè)開頭就說(shuō)過了
def test_persistent(self):pass小課堂:如何控制unittest執(zhí)行的順序呢?
答案:TestSuite(測(cè)試套件)的addTest()方法
unittest的main()方法執(zhí)行用例的順序是按照測(cè)試類、測(cè)試方法的名字的ASCII順序來(lái)執(zhí)行測(cè)試方法。假如不控制unittest執(zhí)行順序,有的有依賴測(cè)試的case就會(huì)運(yùn)行失敗,比如:下單 -> 付款, case的執(zhí)行順序必須要先執(zhí)行下單才能付款,不能反過來(lái)。
假如我們要控制它,有2個(gè)辦法:
1、 通過TestSuite按照順序添加想要執(zhí)行的方法
if __name__ == "__main__":suite = unittest.TestSuite()# 第一種方法:suite.addTest(TestBddClass("test_persistent_c"))suite.addTest(TestBddClass("test_bluetooth_a"))這樣方法的執(zhí)行順序就是先執(zhí)行test_persistent_c,再執(zhí)行test_bluetooth_a
這種方式可以實(shí)現(xiàn),但是你必須要一個(gè)個(gè)手動(dòng)去添加,用例一多就會(huì)你就會(huì)添加的爆炸,不適用。
2、 控制方法名字來(lái)實(shí)現(xiàn)
直接用 test_a_xxx , test_b_xxx, test_c_xxx來(lái)控制。abc換成123也行等等~
例子:
def test_a_persistent(self):u'''persistent進(jìn)程內(nèi)存占用'''persistent, code = self.d.shell("dumpsys meminfo | grep persistent")result = persistent.split("K:")[0].replace(",", "")self.assertLessEqual(int(result), 92160, 'persistent小于90M')def test_b_bluetooth(self):u'''測(cè)試藍(lán)牙狀態(tài), 藍(lán)牙必須默認(rèn)關(guān)閉'''common.open_settings_menu(self.d, "Bluetooth")bluetooth_off = self.d(resourceId="com.android.settings:id/switch_widget", text=u"OFF")self.assertTrue(bluetooth_off, "藍(lán)牙默認(rèn)是關(guān)閉的")如何跳過某個(gè)Case?
如果我們臨時(shí)想要跳過某個(gè)case不執(zhí)行怎么辦?unittest也提供了幾種方法:
# 第一種寫法 class GoogleSecurityCheck(unittest.TestCase):# 跳過測(cè)試類@unittest.skip("I don't want to run this case.")def test_AFW(self):u'''AFW功能支持檢查'''max_user, code = self.d.shell("pm get-max-users")result = max_user.split(':')[-1]self.assertTrue(result, 'get-max-ulser must be greater than 1')class GoogleSecurityCheck(unittest.TestCase):# 跳過測(cè)試casedef test_AFW(self):u'''AFW功能支持檢查'''self.skipTest('Do not run this case')max_user, code = self.d.shell("pm get-max-users")result = max_user.split(':')[-1]self.assertTrue(result, 'get-max-ulser must be greater than 1') 兩種方法得到的結(jié)果是一樣的。運(yùn)行程序,你就會(huì)發(fā)現(xiàn) test_AFW就不會(huì)被執(zhí)行,跳過了。
skip裝飾器有三個(gè):
unittest.skip(reason) unittest.skipIf(condition, reason) unittest.skipUnless(condition, reason) skip無(wú)條件跳過 skipIf當(dāng)condition為True時(shí)跳過 skipUnless當(dāng)condition為False時(shí)跳過。一個(gè)完整的小栗子:
import unittest import timeclass Test(unittest.TestCase):def setUp(self):print ("start!")def tearDown(self):time.sleep(1)print ("end!")def test01(self):print ("執(zhí)行測(cè)試用例01")def test03(self):print ("執(zhí)行測(cè)試用例03")if __name__ == '__main__':# 構(gòu)造測(cè)試集suite = unittest.TestSuite()suite.addTest(Test("test01"))suite.addTest(Test("test03"))# 執(zhí)行測(cè)試runner = unittest.TextTestRunner()runner.run(suite)運(yùn)行結(jié)果:
start! 執(zhí)行測(cè)試用例01 end! start! 執(zhí)行測(cè)試用例03 end! ---------------------------------------------------------------------- Ran 2 tests in 2.000sOK將結(jié)果輸出到文件中:
如上我們的結(jié)果就是輸出到控制臺(tái),運(yùn)行一次得到一次的結(jié)果,要查看之前的歷史記錄就沒辦法,我們可以把結(jié)果輸出到文件。
修改一下代碼:
if __name__ == '__main__':# 構(gòu)造測(cè)試集suite = unittest.TestSuite()suite.addTest(Test("test03"))suite.addTest(Test("test01"))# 執(zhí)行測(cè)試# runner = unittest.TextTestRunner()# runner.run(suite)with open('UnittestTextReport.txt', 'a') as f:runner = unittest.TextTestRunner(stream=f, verbosity=2)runner.run(suite)運(yùn)行結(jié)果:
我猜你嫌棄報(bào)告不夠漂亮,早有人想到了,我們可以用HTMLTestRunner來(lái)生存一個(gè)漂亮的測(cè)試報(bào)告
Note:這種方法適用于python2
from HTMLTestRunner import HTMLTestRunnerwith open('HTMLReport.html', 'w') as f:runner = HTMLTestRunner(stream=f,title='測(cè)試報(bào)告',description='規(guī)范性檢查測(cè)試報(bào)告.',verbosity=2)runner.run(suite)python 3中:
import HTMLTestRunnerif __name__ == '__main__':# 構(gòu)造測(cè)試集suite = unittest.TestSuite()suite.addTest(Test("test03"))suite.addTest(Test("test01"))now = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime())fp = open(now + 'result.html', 'wb')# 定義報(bào)告格式runner = HTMLTestRunner.HTMLTestRunner(stream=fp,title='規(guī)范性檢查測(cè)試報(bào)告',description=u'用例執(zhí)行情況:')# 運(yùn)行測(cè)試用例runner.run(suite)# 關(guān)閉報(bào)告文件fp.close()讓我們看看漂亮的報(bào)告:
代碼中為什么加了如下兩行呢?
now = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime()) fp = open(now + 'result.html', 'wb')因?yàn)闉榱藚^(qū)分報(bào)告,報(bào)告的名稱用當(dāng)前的時(shí)間來(lái)表示,不會(huì)造成重復(fù)。效果如下:
斷言
斷言可以說(shuō)是自動(dòng)化測(cè)試中很重要的了,正確設(shè)置斷言以后才能幫助我們判斷測(cè)試用例執(zhí)行結(jié)果。
例如你寫代碼的時(shí)候,IDE給你的提示:
總結(jié)幾個(gè)常用的就是:
assertEqual(a, b) # 判斷a==b assertNotEqual(a, b) # 判斷a!=b assertTrue(x) # bool(x) is True assertFalse(x) # bool(x) is False assertIs(a, b) # a is b assertIsNot(a, b) # a is not b assertIsNone(x) # x is None assertIsNotNone(x) # x is not None assertIn(a, b) # a in b assertNotIn(a, b) # a not in b assertIsInstance(a, b) # isinstance(a, b) assertNotIsInstance(a, b) # not isinstance(a, b) assertGreater(a, b[, msg]) # 和self.assertTrue(a > b)用法一樣,但是多了設(shè)置條件 .太多了,大家直接自己在編輯器上查查看吧
25.3. unittest - Unit testing framework - Python 2.7.15 documentation?docs.python.org總結(jié)
以上是生活随笔為你收集整理的unittest测试框架_python接口自动化测试 - 4.unittest单元测试框架学习的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PCA方法对特征降维
- 下一篇: 语义分割研究进展