TestCase繼承於 unittest.TestCase
測試方法必須以test開頭html
測試套件: 把多個測試用例集成在一塊兒就是測試套件。
1:實例化測試套件 suite = unittest.TestSuite()
2: 將測試用例加入測試套件 suite.addTest(MyTest(‘test_xxx’))python
測試執行器: 用來執行測試套件
1: 實例化測試執行器: runner = unittest.TextTestRunner()
2: 執行測試套件 : runner.run(suite)數組
測試用例類中實現了前置和後置方法,則這個測試類就是一個Fixture。
例如實現了: setUp() 和 tearDownide
# 1: 導包 import unittest # 2: 準備測試類 class MyTest(unittest.TestCase): def test_1(self): print("這是:test_1") def test_2(self): print("這是:test_2") if __name__ == '__main__': # 3: 將測試用例添加到 測試套件中。 # 實例化測試套件 suite = unittest.TestSuite() # 將測試用例添加到測試套件中。 suite.addTest(MyTest('test_1')) suite.addTest(MyTest('test_2')) # 4:運行容器中的測試用例 # 實例化測試執行器 runner = unittest.TextTestRunner() runner.run(suite)
演示:
函數
# 1: 導包 import unittest # 2: 準備測試類 class MyTest(unittest.TestCase): def test_1(self): print("這是:test_1") def test_2(self): print("這是:test_2") if __name__ == '__main__': # 將當前目錄下的test_1.demo.py中的測試用例所有加載到測試套件。 suite = unittest.defaultTestLoader.discover("./", "test_1_demo.py") # 運行測試用例 runner = unittest.TextTestRunner() runner.run(suite)
""" unittest的基本使用""" # 1: 導入unittest包 import unittest # 2: 建立測試用例類 class MyTest(unittest.TestCase): @classmethod def setUpClass(cls): print("setUpClass") @classmethod def tearDownClass(cls): print("tearDownClass") def setUp(self): print("setup") def tearDown(self): print("tearDown") def test_s(self): print("test_s") a = 1 + 1 self.assertEqual(a, 2, "結果不爲2") def test_b(self): print("test_b") b = 1 + 2 self.assertEqual(b, 2, "結果不爲2") if __name__ == '__main__': unittest.main()
運行結果:單元測試
問題: 若是一個測試用例須要傳遞參數,怎麼傳遞呢??
1: 安裝包:測試
pip install parameterized -i https://pypi.tuna.tsinghua.edu.cn/simple
2: 案例:ui
# 1:導包 import unittest from parameterized import parameterized # 2: 建立測試類 class MyTest(unittest.TestCase): @parameterized.expand([('renshanwen', 23), ('niuniu', 25)]) def test_param(self, name, age): print("name: %s, age: %s" % (name, age)) if __name__ == '__main__': unittest.main()
3: 運行效果:
spa
Mock是Python中一個用於支持單元測試的庫,它的主要功能是使用mock對象替代掉指定的Python對象,以達到模擬對象的行爲。
code
案例: 業務需求: 因爲一段代碼尚未實現,可是此時須要返回一個250的數字,此時須要使用mock代替未實現的代碼,返回一個250的數字,目的是讓測試流程跑通。
# 1:導包 import unittest import unittest.mock # 2: 建立模擬的類 class MyTest(unittest.TestCase): def test_return(self): # 建立一個能返回250的可調用對象 mock_obj = unittest.mock.Mock(return_value=250) # 調用mocke對象,拿到返回值 ret = mock_obj() print(ret) if __name__ == '__main__': unittest.main()
1: 利用side_effect傳遞一個異常:
# 1:導包 import unittest import unittest.mock # 2: 建立模擬的類 class MyTest(unittest.TestCase): def test_return(self): # 建立一個能返回250的可調用對象 mock_obj = unittest.mock.Mock(side_effect=BaseException("自定義異常")) # 調用mocke對象,拿到返回值 ret = mock_obj() print(ret) if __name__ == '__main__': unittest.main()
2:使用side_effect傳遞一個數組:
import unittest.mock # 2: 建立模擬的類 class MyTest(unittest.TestCase): def test_return(self): # 建立一個能返回250的可調用對象 mock_obj = unittest.mock.Mock(side_effect=[1, 2, 3]) # 調用mocke對象,拿到返回值 for i in range(3): print(mock_obj()) if __name__ == '__main__': unittest.main()
3: 使用side_effect傳遞一個函數:
# 1:導包 import unittest import unittest.mock # 2: 建立模擬的類 class MyTest(unittest.TestCase): def test_return(self): def sum_ab(a, b): return a + b # 建立一個能返回250的可調用對象 mock_obj = unittest.mock.Mock(side_effect=sum_ab) # 調用mocke對象,拿到返回值 print(mock_obj(1, 2)) if __name__ == '__main__': unittest.main()
業務需求: 如今有一個需求: A負責開發支付功能,可是A尚未開發完成。B負責支付狀態模塊,已經完成,B代碼中調用A寫的代碼的支付函數了。如今要求C測試B寫的代碼是否符合要求,C如何測試??
答: 使用mock替換A寫的函數,而後調用B寫的函數的時候就會調用咱們的mock,不會調用A未完成的代碼。
import unittest import unittest.mock import pay import pay_status class TestPay(unittest.TestCase): def test_success(self): # 用一個mock來代替pay中的pay_way函數 pay.pay_way = unittest.mock.Mock(return_value={"result": "success", "reason":"null"}) # 盜用要測試的函數 ret = pay_status.pay_way_status() self.assertEqual(ret, '支付成功', '支付失敗') def test_fail(self): pay.pay_way = unittest.mock.Mock(return_value={"result": "fail", "reason":"餘額不足"}) ret = pay_status.pay_way_status() # 調用支付狀態函數 self.assertEqual(ret, '支付失敗', '測試失敗') if __name__ == '__main__': unittest.main()
提示: 當咱們使用 mock 掉一個對象後,默認狀況下,在後面執行的代碼都會受到影響,好比其餘的測試用例,其餘的代碼,只要是這些代碼是在 mock 以後執行的都會受影響。
import unittest import unittest.mock import pay import pay_status class TestPay(unittest.TestCase): def test_success(self): # 用一個mock來代替pay中的pay_way函數 pay.pay_way = unittest.mock.Mock(return_value={"result": "success", "reason":"null"}) # 盜用要測試的函數 ret = pay_status.pay_way_status() self.assertEqual(ret, '支付成功', '支付失敗') def test_success2(self): # 盜用要測試的函數 ret = pay_status.pay_way_status() self.assertEqual(ret, '支付成功', '支付失敗') if __name__ == '__main__': unittest.main()
對於上面的代碼,test_success2,咱們並無使用mock來代替,結果也使用了mock。緣由就是mock一旦被使用,後面執行的測試用例只能使用mock,不能使用原來的函數了。
1: patch裝飾器:
1: 方法上面加上裝飾器: @mock.patch(‘被代替的函數路徑’)
2: 函數另外增長一個傳入值。
3: 函數內部直接使用 傳入值的名字.return_value = {返回值鍵值對}
import unittest from unittest import mock import pay import pay_status class TestPay(unittest.TestCase): @mock.patch('pay.pay_way') def test_success(self, mock_pay_way): # 用一個mock來代替pay中的pay_way函數 mock_pay_way.return_value={"result": "success", "reason":"null"} # 盜用要測試的函數 ret = pay_status.pay_way_status() self.assertEqual(ret, '支付成功', '支付失敗') if __name__ == '__main__': unittest.main()
2: patch上下文管理器:
1: 函數內部使用 : with mock.patch(‘被替代的函數路徑’) as 別名:
2: 別名.return_value = {返回值鍵值對}
import unittest from unittest import mock import pay import pay_status class TestPay(unittest.TestCase): def test_success(self): with mock.patch('pay.pay_way') as mock_pay_way: # 用一個mock來代替pay中的pay_way函數 mock_pay_way.return_value={"result": "success", "reason":"null"} # 盜用要測試的函數 ret = pay_status.pay_way_status() self.assertEqual(ret, '支付成功', '支付失敗') if __name__ == '__main__': unittest.main()
from unittest import mock import unittest class Pay(object): def pay_way(self): """假設這裏是一個支付的功能,未開發完 支付成功返回:{"result": "success", "reason":"null"} 支付失敗返回:{"result": "fail", "reason":"餘額不足"} reason返回失敗緣由 """ raise NotImplementedError('代碼尚未實現') def pay_way_status(self): """根據支付的結果success或fail,判斷跳轉到對應頁面 假設這裏的功能已經開發完成""" # todo 此時pay_way()函數並未完成!你先假定他完成了 result = self.pay_way() print(result) if result["result"] == "success": return "支付成功" if result["result"] == "fail": return "支付失敗"
方案一: 傳統方式:
1: 先實例化這個類
2:對象.被替換函數 = mock.Mock(return_value = {返回的鍵值對})
3:對象.被測函數()
class TestPayStatues(unittest.TestCase): '''單元測試用例''' def test_success1(self): '''測試支付成功場景''' p = Pay() # 實例化對象 p.pay_way = mock.Mock(return_value = {"result": "success", "reason":"null"}) statues = p.pay_way_status() print(statues) self.assertEqual(statues, "支付成功")
方案二: 裝飾器
1: 在測試函數上增長裝飾器:@mock.patch.object(類, ‘被代替的方法名’)
2: 在增長一個函數傳值,def test_success2(self, mock_obj):
3: 使用新傳值.return_value = {返回的鍵值對}
4: 實例化類.被測試函數()
class TestPayStatues(unittest.TestCase): @mock.patch.object(Pay, 'pay_way') def test_success2(self, mock_obj): '''測試支付成功場景''' mock_obj.return_value = {"result": "success", "reason":"null"} # 根據支付結果測試頁面跳轉 statues = Pay().pay_way_status() print(statues) self.assertEqual(statues, "支付成功")
方案三:使用whith上下文
1: with mock.patch.object(類, ‘被代替的方法名’) as 新名字
2:新名字.return_value = {}
3: 實例化類.被測試函數()
class TestPayStatues(unittest.TestCase): def test_success3(self): '''測試支付成功場景''' with mock.patch.object(Pay, 'pay_way') as mock_obj: mock_obj.return_value = {"result": "success", "reason":"null"} # 根據支付結果測試頁面跳轉 statues = Pay().pay_way_status() print(statues) self.assertEqual(statues, "支付成功")
案例測試:
import unittest import unittest.mock class MockTest(unittest.TestCase): def test_return_value(self): mock_obj = unittest.mock.Mock(return_value=1999) result = mock_obj() print(result) # 打印 1999 print(mock_obj.called) # 是否被調用過, 返回布爾值 print(mock_obj.call_count) # 獲取調用測試, 返回調用測試 if __name__ == '__main__': unittest.main()
1: 安裝包:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple htmltestrunner-python3
2:核心代碼:
if __name__ == '__main__': # 1. 把測試用例添加到suite容器中 suite = unittest.defaultTestLoader.discover('./', 'test_1_html.py') # 2. 打開文件,是一個文件對象 with open('./HTMLTestRunner.html', 'w', encoding='utf-8') as f: # 3. HTMLTestRunner()建立一個runner對象 runner = HTMLTestRunner( stream=f, # 測試報告須要寫入到的文件 verbosity=2, # 控制檯輸出信息的詳細程度, 默認爲1 title='這是報告標題', # 測試報告的標題 description='這是一個測試報告' # 測試報告的描述 ) # 4. runner把容器中測試用例運行 runner.run(suite)
1: 安裝包:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple beautifulreport
2: 核心代碼:
if __name__ == '__main__': # 1. 把測試用例添加到suite容器中 suite = unittest.defaultTestLoader.discover('./', 'test_2_beautiful.py') # 2. 建立runner對象,同時把suite傳參進入 runner = BeautifulReport(suite) # 3. 運行,同時生成測試報告 # 參數1:生成文件的註釋, 參數2:生成文件的filename, 參數3:生成report的文件存儲路徑 runner.report('報告描述必須有,在報告中顯示爲用例名稱', '測試報告文件名', './')