目錄
- unittest介紹
-
- TestCase編寫
- TestSuite與TextTestRunner
- TestLoader
- TestFixture
-
- setUp和tearDown
- setUpClass和tearDownClass
- 斷言(重點)
-
- assertEqual(a, b) 和 assertNotEqual(a, b)
- assertTrue(x)和assertFalse(x)
- assertIn(a, b)和assertNotIn(a, b)
- 測試報告
-
- 檔案方式的測試報告
- HTML方式的測試報告
- skip跳過用例
unittest介紹
unittest是Python自帶的一個單元測試架構, 它可以做單元測試, 也能用于編寫和運作重複的測試工作.
它給自動化測試用例開發和執行提供了豐富的斷言方法, 判斷測試用例是否通過, 并最終生成測試結果.
TestCase編寫
- TestCase指的就是測試用例
- 測試類必須繼承unittest.TestCase
- 測試方法名稱命名必須以test開頭
- 測試方法的執行順序有Case序号決定, 并非由代碼順序決定
一個簡單的例子:
# 1. 導入unittest
import unittest
# 2. 建立類繼承unittest.TestCase
class Test(unittest.TestCase):
# 3. 建立測試用例方法, 方法要以test開頭
# 執行順序是根據case序号來的, 并非代碼的順序
def test_add_01(self):
print(3+2)
def test_add_02(self):
print(10+5)
TestSuite與TextTestRunner
在上面提到, 測試用例的執行順序預設是由case序号決定的,但也可以使用TestSuite控制用例的執行順序。
- TestSuite(測試套件)可以組織多個測試用例
- TextTestRunner測試用例運作器
- run()方法是測試用例的執行, 入參為suite測試套件
一個簡單的例子
# 1. 導入unittest
import unittest
# 2. 建立類繼承unittest.TestCase
class Test(unittest.TestCase):
# 3. 建立測試用例方法, 方法要以test開頭
# 執行順序是根據case序号來的, 并非代碼的順序
def test_add_01(self):
print(3+2)
def test_add_02(self):
print(10+5)
if __name__ == '__main__':
suite = unittest.TestSuite() # 執行個體化TestSuite
suite.addTest(Test("test_add_02")) # 添加測試用例
suite.addTest(Test("test_add_01"))
runner = unittest.TextTestRunner() # 執行個體化TextTestRunner
runner.run(suite) # 傳入suite并執行測試用例
但是要注意, 如果在Pychram中直接右鍵運作, 它是不會執行main下面的内容, 需要修改pychram的運作方式
然後重新運作, 就會執行main下面的内容了.
重點,通常測試用例都是多個的,是以測試用例可以存放到清單中一次性添加到測試套件。這裡可以使用
suite.addTests()
case_list = ['Test("test_add_02")', Test("test_add_01")]
suite.addTests(case_list)
TestLoader
如果有多個測試檔案時, 可以使用TestLoader加載測試用例
-
, 使用discover()去加載測試用例unittest.defaultTestLoader
- 找到指定目錄下所有測試子產品,并可遞歸查到子目錄下的測試子產品,隻有比對到檔案名才能被加載
- discover(start_dir, pattern=‘test*.py’, top_level_dir=None)
- start_dir:要測試的子產品名貨測試用例目錄
- pattern=“test*.py”: 表示用例檔案名的比對原則。 此處比對檔案名以“test”開頭的“.py”類型的檔案,*表示任意字元
- top_level_dir=None:測試子產品的頂層目錄, 如果沒有頂層目錄, 預設為None
一個小例子, 再目錄下建立多個test開頭的py檔案, 并且寫入測試方法
然後使用discover()方法讀取測試用例并執行
import unittest
# dicsover方法查找用例
suite = unittest.defaultTestLoader.discover("unittest_test", "test*.py")
# 2.TextTestRunner運作用例
runer = unittest.TextTestRunner()
runer.run(suite)
TestFixture
setUp和tearDown
setUp()方法
- 主要是用來初始化測試環境, 它在每條測試用例執行前都會調用
tearDown()方法
- 主要作用是測試用例執行完畢後恢複測試環境, 即使出現異常也會調用此方法,每條用例執行結束後都會運作
一個簡單的例子
import unittest
class Test(unittest.TestCase):
def setUp(self) -> None: # 調用setUp
super().setUp()
print("測試用例執行前操作")
def test_add_01(self):
print("num02")
def test_add_02(self):
print("num03")
def tearDown(self) -> None: # 調用tearDown
super().tearDown()
print("測試用例執行後操作")
# 傳回結果
測試用例執行前操作
num02
測試用例執行後操作
測試用例執行前操作
num03
測試用例執行後操作
在上面的傳回結果中可以明确的看出, 每條用例執行前都會先運作setUp,執行結束後都會運作tearDown
setUpClass和tearDownClass
setUpClass
- 初始化測試環境且隻會執行一次。在類中需要加上@classmethod
tearDownClass
- 恢複測試環境且隻會執行一次。在類中需要加上@classmethod
import unittest
class Test(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
print("測試前的操作")
@classmethod
def tearDownClass(cls) -> None:
super().tearDownClass()
print("測試後的操作")
def test_add_01(self):
print(3+2)
def test_add_02(self):
print(10+5)
# 傳回結果
測試前的操作
15
5
測試後的操作
從傳回結果可以看到setUpClass和tearDownClass都隻是執行了一次,與setUp和tearDown的結果有很明顯的差別。
注意,setUpClass和tearDownClass執行一次是針對目前的測試類而言的,如果目前的py檔案有多個測試類, 那麼它每個測試類都會執行一次。
如下:
# 1. 導入unittest
import unittest
# 2. 建立類繼承unittest.TestCase
class Test(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
print("測試前的操作")
@classmethod
def tearDownClass(cls) -> None:
super().tearDownClass()
print("測試後的操作")
# 3. 建立測試用例方法, 方法要以test開頭
# 執行順序是根據case序号來的, 并非代碼的順序
def test_add_01(self):
print(3+2)
def test_add_02(self):
print(10+5)
class Test2(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
print("測試前的操作2")
@classmethod
def tearDownClass(cls) -> None:
super().tearDownClass()
print("測試後的操作2")
def test_add_03(self):
print(20)
def test_add_04(self):
print(40)
if __name__ == '__main__':
suite = unittest.TestSuite() # 執行個體化TestSuite
suite.addTests([Test("test_add_02"), Test("test_add_01"), Test2("test_add_03"), Test2("test_add_04")]) # 添加測試用例
runner = unittest.TextTestRunner() # 執行個體化TextTestRunner
runner.run(suite) # 傳入suite并執行測試用例
斷言(重點)
常用的斷言
方法 | 檢查 |
---|---|
assertEqual(a, b) | a == b |
assertNotEqual(a, b) | a != b |
assertTrue(x) | x的布爾值為真 |
assertFalse(x) | x的布爾值為假 |
assertIn(a, b) | a in b |
assertNotIn(a, b) | a not in b |
assertEqual(a, b) 和 assertNotEqual(a, b)
# 1. 導入unittest
import unittest
# 2. 建立類繼承unittest.TestCase
class Test(unittest.TestCase):
def setUp(self) -> None:
super().setUp()
print("測試開始")
def test_add_01(self):
print("1 == 1")
self.assertEqual(1, 1) # 成功
def test_add_02(self):
print("1 == 2")
self.assertEqual(1, 2) # 失敗
def test_add_03(self):
print("1 !=2 ")
self.assertNotEqual(1, 2) # 成功
def test_add_04(self):
print("1 != 1")
self.assertNotEqual(1, 1) # 失敗
def tearDown(self) -> None:
super().tearDown()
print("測試結束")
assertTrue(x)和assertFalse(x)
class Test(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
print("測試前的操作2")
@classmethod
def tearDownClass(cls) -> None:
super().tearDownClass()
print("測試後的操作2")
def test_05(self):
self.assertTrue(1 < 2) # 成功
def test_06(self):
self.assertTrue(1 > 2) # 失敗
def test_07(self):
self.assertFalse(1 > 2) # 成功
def test_08(self):
self.assertFalse(1 < 2) # 失敗
assertIn(a, b)和assertNotIn(a, b)
import unittest
class Test(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
print("測試前的操作2")
@classmethod
def tearDownClass(cls) -> None:
super().tearDownClass()
print("測試後的操作2")
def test_09(self):
self.assertIn("a", "abc") # 成功
def test_10(self):
self.assertIn("a", "bcd") # 失敗
def test_11(self):
self.assertNotIn("a", "bcd") # 成功
def test_12(self):
self.assertNotIn("a", "abc") # 失敗
測試報告
檔案方式的測試報告
import unittest
# dicsover方法查找用例
suite = unittest.defaultTestLoader.discover("unittest_test", "test*.py")
# 打開檔案對象
with open("test_report.txt", "a") as f:
# TextTestRunner運作用例
runer = unittest.TextTestRunner(stream=f, verbosity=2) # verbosity=2 輸出詳細日志
runer.run(suite)
HTML方式的測試報告
HTML報告需要導入HTMLTestRunner.py,這個檔案原本隻支援python2,python3需要修改部分代碼才能使用。
然後我發現CSDN這裡已經有人上傳過這個資源了, 不過貌似要收費, 我這裡也傳一個網盤吧, 這是已經修改好支援python的, 直接使用就行。
連結:https://pan.baidu.com/s/171jiRp9lw8Gtrl140g0uJw
提取碼:64db
回到正題,HTMLTestRunner的用法其實和TextTestRunner的差不多, 代碼如下:
import unittest
from lib.HTMLTestRunner import HTMLTestRunner
# dicsover方法查找用例
suite = unittest.defaultTestLoader.discover("unittest_test", "test*.py")
with open("test_report.html", "wb") as report:
runner = HTMLTestRunner(stream=report, verbosity=2, title="HTML測試報告", description="這是練習的測試報告")
runner.run(suite)
運作結束後在本地就會看到多了一個HTML檔案,打開就能看到測試報告了
skip跳過用例
在遇到不想執行的測試用例時,可以使用skip方法
-
:無條件跳過用例, reason是說明原因@unittest.skip(reason)
-
:condition為true時跳過用例@unittest.skipIf(condition, reason)
-
:condition為False的時候跳過@ unittest.skipUnless(condition, reason)
執行個體代碼:
# 1. 導入unittest
import unittest
class Test(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
print("測試前的操作2")
@classmethod
def tearDownClass(cls) -> None:
super().tearDownClass()
print("測試後的操作2")
@unittest.skip("無條件跳過")
def test_09(self):
self.assertIn("a", "abc")
@unittest.skipIf(2>1, "因為2>1是以跳過")
def test_10(self):
self.assertIn("a", "bcd")
@unittest.skipUnless(1>2, "因為1>2為假,是以跳過")
def test_11(self):
self.assertNotIn("a", "bcd")
def test_12(self):
self.assertNotIn("a", "abc")
然後執行用例并生成測試報告
import unittest
from lib.HTMLTestRunner import HTMLTestRunner
# dicsover方法查找用例
suite = unittest.defaultTestLoader.discover("unittest_test", "test*.py")
with open("test_report.html", "wb") as report:
runner = HTMLTestRunner(stream=report, verbosity=2, title="HTML測試報告", description="這是練習的測試報告")
runner.run(suite)
測試報告,隻剩下了一個測試用例了