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