UnitTest單元測(cè)試框架
一、單元測(cè)試
-
單元測(cè)試概念
單元測(cè)試(unit testing),是指對(duì)軟件中的最小可測(cè)試單元進(jìn)行檢查和驗(yàn)證
對(duì)于單元測(cè)試中單元的含義,要根據(jù)實(shí)際情況去判定其具體含義
一個(gè)單元可能是功能模塊、類、方法(函數(shù))等
-
單元測(cè)試工具
不同的編程語(yǔ)言都有比較成熟的單元測(cè)試框架,語(yǔ)法規(guī)則有些差別,其核心思想都是相通的,常見(jiàn)的單元測(cè)試框架有:
2.1 Java語(yǔ)言:Junit、TestNG
2.2 Python語(yǔ)言:UnitTest、Pytest
二、UnitTest框架概念
-
介紹
UnitTest是Python自帶的一個(gè)單元測(cè)試框架,用它來(lái)做單元測(cè)試。也經(jīng)常應(yīng)用到UI自動(dòng)化測(cè)試和接口自動(dòng)化測(cè)試中,用來(lái)管理和維護(hù)測(cè)試用例腳本。
-
使用UnitTest框架的好處
2.1 能夠組織多個(gè)用例去執(zhí)行(可以把多條測(cè)試用例封裝成一個(gè)測(cè)試套件,實(shí)現(xiàn)批量執(zhí)行測(cè)試用例)
2.2 提供了豐富的斷言方法,方便對(duì)用例執(zhí)行的結(jié)果進(jìn)行判斷
2.3 能夠生成HTML格式的測(cè)試報(bào)告
2.4 使用Fixture功能可以減少代碼的冗余
-
UnitTest核心要素
3.1 TestCase:測(cè)試用例
3.2 TestSuite:測(cè)試套件,多條測(cè)試用例集合在一起
3.3 TestRunner:執(zhí)行測(cè)試用例和測(cè)試套件
三、UnitTest使用
1. TestCase測(cè)試用例
-
案例
1.1 定義一個(gè)實(shí)現(xiàn)加法操作的函數(shù),并對(duì)該函數(shù)進(jìn)行測(cè)試
# test01_add.py import unittest def add(x, y): return x + y # 必須要集成TestCase類 class TestAdd(unittest.TestCase): # 定義測(cè)試方法1 def test01_add(self): result = add(1, 1) print('result1=', result) # 定義測(cè)試方法2 def test02_add(self): result = add(2, 2) print('result2=', result)1.2 如何執(zhí)行測(cè)試用例
使用pycharm在代碼上點(diǎn)擊鼠標(biāo)右鍵,選擇使用UnitTest運(yùn)行
2. TestSuite
-
說(shuō)明
測(cè)試套件可以將多條測(cè)試用例集合在一起,就是一個(gè)TestSuite使用
-
使用
2.1 實(shí)例化:suite = unittest.TestSuite() (suite:為T(mén)estSuite實(shí)例化的名稱)
2.2 添加用例:suite.addTest(ClassName("MethodName"))(ClassName:為類名;MethodName:為方法名)
2.3 添加擴(kuò)展:suite.addTest(unittest.makeSuite(ClassName))(搜索指定ClassName內(nèi)test開(kāi)頭的方法并添加到測(cè)試套件中)
注:TestSuite需要配合TestRunner才能被執(zhí)行
3. TextTestRunner
-
說(shuō)明
TextTestRunner是用來(lái)執(zhí)行測(cè)試用例和測(cè)試套件的使用
-
使用流程
2.1 實(shí)例化:runner = unittest.TextTestRunner()
2.2 .執(zhí)行:runner.run(suite) suite:為測(cè)試套件名稱
-
綜合示例
3.1 創(chuàng)建第2個(gè)測(cè)試模塊:test_login.py
# test02_login.py import unittest class TestLogin(unittest.TestCase): def test01_login_case(self): print("username is wrong") def test02_login_case(self): print('password is wrong') def test03_login_case(self): print('login success')3.2 同時(shí)運(yùn)行多個(gè)測(cè)試模塊的測(cè)試用例:run_suite.py
# run_suite.py # 添加多個(gè)測(cè)試套件,運(yùn)行測(cè)試用例 import unittest from test01_add import TestAdd from test02_sub import TestLogin #實(shí)例化測(cè)試套件對(duì)象 suite = unittest.TestSuite() # 添加測(cè)試用例 suite.addTest(TestAdd("test01_add")) suite.addTest(TestAdd("test02_add")) suite.addTest(unittest.makeSuite(TestLogin)) #實(shí)例化運(yùn)行器對(duì)象 runner = unittest.TextTestRunner() #運(yùn)行測(cè)試套件 runner.run(suite)
4. Fixture
-
說(shuō)明
Fixture是對(duì)一個(gè)測(cè)試用例環(huán)境的初始化和銷毀操作
-
方法級(jí)別使用
2.1 初始化 ( 前置處理 )
def setUp(self) --> 首先自動(dòng)執(zhí)行
2.2 銷毀 ( 后置處理 )
def tearDown(self) --> 最后自動(dòng)執(zhí)行
2.3 運(yùn)行于測(cè)試方法的始末,
即:運(yùn)行一次測(cè)試方法就會(huì)運(yùn)行一次 setUp 和 tearDown
-
類級(jí)別的使用
3.1 初始化 ( 前置處理 ):
@classmethod
def setUpClass(cls): --> 首先自動(dòng)執(zhí)行
3.2 銷毀 ( 后置處理 ):
@classmethod
def tearDownClass(cls): --> 最后自動(dòng)執(zhí)行
3.3 運(yùn)行于測(cè)試類的始末,
即:每個(gè)測(cè)試類只會(huì) 運(yùn)行一次 setUpClass 和 tearDownClass
-
示例
4.1 模擬達(dá)達(dá)商城注冊(cè)模塊自動(dòng)化測(cè)試用例-類級(jí)別的fixture
import unittest class TestFixture(unittest.TestCase): # 類級(jí)別,初始化、前置處理,測(cè)試開(kāi)始時(shí)只執(zhí)行一次 @classmethod def setUpClass(cls): print('自動(dòng)打開(kāi)瀏覽器并進(jìn)入注冊(cè)頁(yè)') # 類級(jí)別,銷毀、后置處理,測(cè)試結(jié)束時(shí)只執(zhí)行一次 @classmethod def tearDownClass(cls): print('自動(dòng)關(guān)閉瀏覽器,執(zhí)行測(cè)試收尾工作') def test01_register(self): # 測(cè)試用例1:注冊(cè)時(shí)用戶名格式輸入錯(cuò)誤 print('refresh register html') print('test01: username is error') def test02_register(self): # 測(cè)試用例2:注冊(cè)時(shí)手機(jī)號(hào)填寫(xiě)錯(cuò)誤 print('refresh register html') print('test02: mobile is error') def test03_register(self): # 測(cè)試用例3:注冊(cè)時(shí)密碼格式填寫(xiě)錯(cuò)誤 print('refresh register html') print('test03: password length is error') def test04_register(self): # 測(cè)試用例4:注冊(cè)時(shí)郵箱格式填寫(xiě)錯(cuò)誤 print('refresh register html') print('test04: email is error')4.2 模擬達(dá)達(dá)商城登錄模塊自動(dòng)化測(cè)試用例-方法級(jí)別的fixture
import unittest class TestLoginFixture(unittest.TestCase): # 每個(gè)測(cè)試方法執(zhí)行時(shí)都會(huì)執(zhí)行此函數(shù) def setUp(self): print('自動(dòng)化打開(kāi)了瀏覽器') # 每個(gè)測(cè)試方法結(jié)束時(shí)都會(huì)執(zhí)行次函數(shù) def tearDown(self): print('自動(dòng)化關(guān)閉了瀏覽器') def test01_login(self): # 用戶名輸入錯(cuò)誤 print('username is not exists') def test02_login(self): # 密碼輸入錯(cuò)誤 print('password is wrong') def test03_login(self): # 都正確,登錄成功 print('congratulations, login success')
5. UnitTest斷言
-
說(shuō)明
讓程序代替人為判斷測(cè)試程序執(zhí)行結(jié)果是否符合預(yù)期結(jié)果的過(guò)程
UnitTest 中提供了非常豐富的斷言方法,但是常用的也就那么幾個(gè),并且使用起來(lái)也比較簡(jiǎn)單
-
斷言方法
-
使用示例
斷言方法經(jīng)在unittest.TestCase 類中定義好了,而且我們自定義的測(cè)試類已經(jīng)繼承了 TestCase ,所以在測(cè)試方法中直接調(diào)用即可。
import unittest def add(x, y): return x + y class TestAssert(unittest.TestCase): def setUp(self): print('方法初始化操作') def tearDown(self): print('方法收尾操作') def test01(self): # 測(cè)試用例1 result = add(1, 1) self.assertEqual(2, result) def test02(self): # 測(cè)試用例2 result = add(2, 2) is_ok = result == 4 self.assertTrue(is_ok) def test03(self): result = add(3, 3) result = str(result) self.assertIn(result, "123456")
6. UnitTest參數(shù)化
-
說(shuō)明
1.1 通過(guò)參數(shù)的方式來(lái)傳遞數(shù)據(jù),從而實(shí)現(xiàn)數(shù)據(jù)和腳本分離,也可以把測(cè)試數(shù)據(jù)定義到數(shù)據(jù)文件或者數(shù)據(jù)庫(kù)中
1.2 針對(duì)同一個(gè)測(cè)試方法,可以實(shí)現(xiàn)用例的重復(fù)執(zhí)行,減少代碼冗余,提高測(cè)試效率
1.3 unittest測(cè)試框架,本身不支持參數(shù)化,但是可以通過(guò)安裝 unittest 擴(kuò)展插件rameterized 來(lái)實(shí)現(xiàn)
-
環(huán)境安裝
sudo pip3 install parameterized
-
使用
3.1 導(dǎo)包
from parameterized import parameterized
3.2 使用裝飾器裝飾需要傳參的測(cè)試用例函數(shù)
@parameterized.expand([(1,1,2),(2,2,4)])
def test01_add(self, x, y, expect):
pass -
示例
import unittest from parameterized import parameterized # 方式二:構(gòu)建測(cè)試數(shù)據(jù) def build_data(): return [('token001', 1),('token002', 2),('token003', 3)] class TestCarts(unittest.TestCase): # 方式一:手動(dòng)定義需要傳參數(shù)據(jù) [(),(),...] @parameterized.expand([('token001', 1),('token002', 2)]) def test01_carts(self, token, number): print('test01: token=%s number=%d' % (token, number)) self.assertEqual(200, {'code':200}.get('code')) # 方式二:定義函數(shù),生成測(cè)試數(shù)據(jù),可以是從json文件讀取,也可以是從數(shù)據(jù)庫(kù) @parameterized.expand(build_data) def test02_carts(self, token, number): print('test02: token=%s number=%d' % (token, number)) self.assertEqual(200, {'code':200}.get('code'))
7. 生成HTML測(cè)試報(bào)告
-
說(shuō)明
測(cè)試腳本執(zhí)行完后,可以生成以 HTML( 網(wǎng)頁(yè) ) 格式的測(cè)試報(bào)告
-
為什么要測(cè)試報(bào)告
2.1 測(cè)試報(bào)告是本次測(cè)試結(jié)果的體現(xiàn)形態(tài)
2.2 測(cè)試報(bào)告內(nèi)包含了有關(guān)本次測(cè)試用例的詳情
-
使用方法
使用HTMLTestRunner 生成測(cè)試報(bào)告
3.1 復(fù)制 HTMLTestRunner.py 文件到項(xiàng)目文件夾
3.2 導(dǎo)入HTMLTestRunner、unittest 包
3.3 生成測(cè)試套件
suite = unittest.TestSuite()
suite.addTest(TestAdd("test_01"))
3.4 設(shè)置報(bào)告生成路徑和文件名 file_name = "./report/report.html"
3.5 打開(kāi)報(bào)告 with open(file_name,'wb') a s f:
3.6 實(shí)例化 HTMLTestRunner 對(duì)象
runner = HTMLTestRunner(stream=f,[title],[description])
參數(shù)說(shuō)明:
stream :文件流,打開(kāi)寫(xiě)入報(bào)告的名稱及 寫(xiě)入編碼格式 ) title : [ 可選參數(shù) ] ,為報(bào)告標(biāo)題 description : [ 可選參數(shù) ] ,為報(bào)告描 述信息3.7 執(zhí)行: runner.run(suite)
-
使用示例
# run_suite.py # 導(dǎo)包 import unittest from test01_add import TestAdd from tools.HTMLTestRunner import HTMLTestRunner #實(shí)例化測(cè)試套件對(duì)象 suite=unittest.TestSuite() # 添加測(cè)試用例 suite.addTest(unittest.makeSuite(TestAdd)) # 定義測(cè)試報(bào)告存放路徑(提前創(chuàng)建report文件夾) report_path = './report/report.html' # 打開(kāi)文件流 with open(report_path, 'wb') as f: # 實(shí)例化運(yùn)行器對(duì)象 runner = HTMLTestRunner(f, title='測(cè)試報(bào)告') #運(yùn)行測(cè)試套件 runner.run(suite)
