什么是框架?
1. 框架英文單詞framework
2. 為解決一類事情的功能集合
是Python自帶的一個(gè)單元測(cè)試框架
- ?帶的, 可以直接使?, 不需要單外安裝
- 測(cè)試?員 ?來(lái)做?動(dòng)化測(cè)試, 作為?動(dòng)化測(cè)試的執(zhí)?框架, 即 管理和執(zhí)??例的
1. 能夠組織多個(gè)用例去執(zhí)行
2. 提供豐富的斷言方法
3. 能夠生成測(cè)試報(bào)告
1. TestCase 測(cè)試?例, 這個(gè)測(cè)試?例是 unittest 的組成部分,作?是 ?來(lái)書(shū)寫真正的?例代碼(腳本)
2. Testsuite 測(cè)試套件, 作?是?來(lái)組裝(打包)TestCase(測(cè)試?例) 的,即 可以將多個(gè)?例腳本?件 組裝到?起
3. TestRunner 測(cè)試執(zhí)?(測(cè)試運(yùn)?), 作? 是?例執(zhí)?TestSuite(測(cè)試套件)的
4. TestLoader 測(cè)試加載, 是對(duì) TestSuite(測(cè)試套件) 功能的補(bǔ)充, 作?是?來(lái)組裝(打包) TestCase(測(cè)試?例) 的
5. Fixture 測(cè)試夾具, 是?種代碼結(jié)構(gòu), 書(shū)寫 前置?法(執(zhí)??例之前的?法)代碼 和后置?法(執(zhí)??例之后的?法) 代碼 ,即 ?例執(zhí)?順序 前置 ---> ?例 ---> 后置
步驟:
1. 導(dǎo)包 unittest
2. 定義測(cè)試類, 需要繼承 unittest.TestCase 類, 習(xí)慣性類名以 Test 開(kāi)頭
3. 書(shū)寫測(cè)試?法, 必須以 test 開(kāi)頭
4. 執(zhí)?
注意事項(xiàng)
1. 代碼?件名字 要滿?標(biāo)識(shí)符的規(guī)則
2. 代碼?件名 不要使?中?
代碼
# 1. 導(dǎo)包
import unittest
# 2. 定義測(cè)試類:新建 測(cè)試類 ;只要繼承unittest.TestCase類,就是測(cè)試類
class TestDemo(unittest.TestCase):
# 3. 定義測(cè)試方法:測(cè)試方法名稱命名必須以test開(kāi)頭
def test_01(self):
print("測(cè)試一")
def test_02(self):
print("測(cè)試二")
# 4.執(zhí)行
# 4.1 在類名或者?法名后邊右鍵運(yùn)?
# 4.1.1 在類名后邊, 執(zhí)?類中的所有的測(cè)試?法
# 4.1.2 在?法名后邊, 只執(zhí)?當(dāng)前的測(cè)試?法
將多條?例腳本集合在?起,就是套件, 即?來(lái)組裝?例的
步驟
1. 導(dǎo)包 unittest
2. 實(shí)例化套件對(duì)象 unittest.TestSuite()
3. 添加?例?法
?來(lái)執(zhí)?套件對(duì)象
步驟
1. 導(dǎo)包 unittest
2. 實(shí)例化 執(zhí)?對(duì)象 unittest.TextTestRunner()
3. 執(zhí)?對(duì)象執(zhí)? 套件對(duì)象 執(zhí)?對(duì)象.run(套件對(duì)象)
1. 導(dǎo)包 unittest
2. 實(shí)例化套件對(duì)象 unittest.TestSuite()
3. 添加?例?法
- 3.1 套件對(duì)象.addTest(測(cè)試類名('測(cè)試?法名'))
4. 實(shí)例化 執(zhí)?對(duì)象 unittest.TextTestRunner()
5. 執(zhí)?對(duì)象執(zhí)? 套件對(duì)象 執(zhí)?對(duì)象.run(套件對(duì)象)
# 1. 導(dǎo)包 unittest
import unittest
from TestCase import TestDemo
from TestCase_01 import TestDemo01
# 2. 實(shí)例化套件對(duì)象 unittest.TestSuite()
suite = unittest.TestSuite()
# 3. 添加?例?法
# - 3.1 套件對(duì)象.addTest(測(cè)試類名('測(cè)試?法名'))
suite.addTest(TestDemo('test_01'))
suite.addTest(TestDemo('test_02'))
suite.addTest(TestDemo01('test_03'))
suite.addTest(TestDemo01('test_04'))
# - 3.2套件對(duì)象.addTest(unittest.makeSuite(測(cè)試類名)) # 在
不同的 Python 版本中,可能沒(méi)有提示
suite.addTest(unittest.makeSuite(TestDemo1))
suite.addTest(unittest.makeSuite(TestDemo2))
# - 3.3
suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestDemo1))
suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestDemo2))
# 4. 實(shí)例化 執(zhí)?對(duì)象 unittest.TextTestRunner()
runner = unittest.TextTestRunner()
# 5. 執(zhí)?對(duì)象執(zhí)? 套件對(duì)象 執(zhí)?對(duì)象.run(套件對(duì)象)
runner.run(suite)

作?和 TestSuite 作??樣,組裝?例代碼, 同樣也需要使?TextTestRunner() 去執(zhí)?
需要使用10 個(gè)?例腳本 makeSuite()
使用unittest.TestLoader,通過(guò)該類下面的discover()方法自動(dòng)搜索指定目錄下指定開(kāi)頭的.py文件,并將查找到的測(cè)試用例組裝到測(cè)試套件。
步驟
1. 導(dǎo)包 unittest
2. 實(shí)例化加載對(duì)象并加載?例 ---> 得到的是 套件對(duì)象(最終返回的還是測(cè)試套件對(duì)象,運(yùn)行測(cè)試套件還需要使用TestRunner)
3. 實(shí)例化執(zhí)?對(duì)象并執(zhí)?
import unittest
# 實(shí)例化加載對(duì)象并加載?例,得到套件對(duì)象
# suite = unittest.TestLoader().discover('?例所在的?錄', '?例代碼?件名*.py')
suite = unittest.TestLoader().discover("./", "hm_02*.py")
# runner=unittest.TextTestRunner()
# runner.run(suite)
unittest.TextTestRunner().run(suite)
1. 創(chuàng)建?個(gè)?錄 case, 作?就是?來(lái)存放?例腳本,
2. 在這個(gè)?錄中創(chuàng)建 5 個(gè)?例代碼?件 , test_case1.py
...
3. 使? TestLoader 去執(zhí)??例
-----
將來(lái)的代碼 ?例都是單獨(dú)的?錄 中存放的
test_項(xiàng)?_模塊_功能.py
import unittest
suite = unittest.TestLoader().discover('case', 'test_case*.py')
unittest.TextTestRunner().run(suite)
1. 定義?個(gè) tools 模塊, 在這個(gè)模塊中 定義 add 的?法,可
以對(duì)兩個(gè)數(shù)字求和,返回求和結(jié)果
2. 書(shū)寫?例, 對(duì) add() 函數(shù)進(jìn)?測(cè)試
1, 1, 2
1, 2, 3
3, 4, 7
4, 5, 9
-----
之前的測(cè)試?法,直接?個(gè) print
這個(gè)案例中的 測(cè)試?法,調(diào)? add 函數(shù), 使? if 判斷,來(lái)判斷
預(yù)期結(jié)果和實(shí)際結(jié)果是否相符
預(yù)期結(jié)果 2 3 7 9
實(shí)際結(jié)果 調(diào)? add()
import unittest
from tools import add
class testadd(unittest.TestCase):
def test1(self):
"""1,1,2"""
print(f"測(cè)試數(shù)據(jù)為:{1},{1},{2}")
if 2==add(1,1):
print(f"測(cè)試數(shù)據(jù)為:{1},{1},{2}通過(guò)")
else:
print(f"測(cè)試數(shù)據(jù)為:{1},{1},{2}不通過(guò)")
def test2(self):
"""1,2,3"""
print(f"測(cè)試數(shù)據(jù)為:{1},{2},{3}")
if 3==add(1,2):
print(f"測(cè)試數(shù)據(jù)為:{1},{2},{3}通過(guò)")
else:
print(f"測(cè)試數(shù)據(jù)為:{1},{2},{3}不通過(guò)")
def test3(self):
"""3,4,7"""
print(f"測(cè)試數(shù)據(jù)為:{3},{4},{7}")
if 3==add(1,2):
print(f"測(cè)試數(shù)據(jù)為:{3},{4},{7}通過(guò)")
else:
print(f"測(cè)試數(shù)據(jù)為:{3},{4},{7}不通過(guò)")
def test4(self):
"""4,5,9"""
print(f"測(cè)試數(shù)據(jù)為:{4},{5},{9}")
if 3==add(1,2):
print(f"測(cè)試數(shù)據(jù)為:{4},{5},{9}通過(guò)")
else:
print(f"測(cè)試數(shù)據(jù)為:{4},{5},{9}不通過(guò)")
代碼結(jié)構(gòu), 在?例執(zhí)?前后會(huì)?動(dòng)執(zhí)?的代碼結(jié)構(gòu)
tpshop 登錄
1. 打開(kāi)瀏覽器 (?次)
2. 打開(kāi)??,點(diǎn)擊登錄 (每次)
3. 輸??戶名密碼驗(yàn)證碼1,點(diǎn)擊登錄 (每次, 測(cè)試?法)
4. 關(guān)閉?? (每次)
2. 打開(kāi)??,點(diǎn)擊登錄 (每次)
3. 輸??戶名密碼驗(yàn)證碼2,點(diǎn)擊登錄 (每次, 測(cè)試?法)
4. 關(guān)閉?? (每次)
2. 打開(kāi)??,點(diǎn)擊登錄 (每次)
3. 輸??戶名密碼驗(yàn)證碼3,點(diǎn)擊登錄 (每次, 測(cè)試?法)
4. 關(guān)閉?? (每次)
5. 關(guān)閉瀏覽器 (?次)
在每個(gè)?例執(zhí)?前后都會(huì)?動(dòng)調(diào)?, ?法名是固定的
def setUp(self): # 前置
# 每個(gè)?例執(zhí)?之前都會(huì)?動(dòng)調(diào)?
pass
def tearDown(self): # 后置
# 每個(gè)?例執(zhí)?之后 都會(huì)?動(dòng)調(diào)?
pass
# ?法前置 ?例 ?法后置
# ?法前置 ?例 ?法后置
在類中所有的測(cè)試?法執(zhí)?前后 會(huì)?動(dòng)執(zhí)?的代碼, 只執(zhí)??次
# 類級(jí)別的 Fixture 需要寫作類?法
@classmethod
def setUpClass(cls): # 類前置
pass
@classmethod
def tearDownClass(cls): # 后置
pass
# 類前置 ?法前置 ?例 ?法后置 ?法前置 ?例 ?法后置類后置
模塊, 就是代碼?件
模塊級(jí)別 在這個(gè)代碼?件執(zhí)?前后執(zhí)??次
# 在類外部定義函數(shù)
def setUpModule():
pass
def tearDownModule():
pass
實(shí)現(xiàn):
tpshop 登錄
1. 打開(kāi)瀏覽器 (?次)
2. 打開(kāi)??,點(diǎn)擊登錄 (每次)
3. 輸??戶名密碼驗(yàn)證碼1,點(diǎn)擊登錄 (每次, 測(cè)試?法)
4. 關(guān)閉?? (每次)
2. 打開(kāi)??,點(diǎn)擊登錄 (每次)
3. 輸??戶名密碼驗(yàn)證碼2,點(diǎn)擊登錄 (每次, 測(cè)試?法)
4. 關(guān)閉?? (每次)
2. 打開(kāi)??,點(diǎn)擊登錄 (每次)
3. 輸??戶名密碼驗(yàn)證碼3,點(diǎn)擊登錄 (每次, 測(cè)試?法)
4. 關(guān)閉?? (每次)
5. 關(guān)閉瀏覽器 (?次)
import unittest
class TestLogin(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print("1.打開(kāi)瀏覽器")
def setUp(self) -> None:
print("2.打開(kāi)??, 點(diǎn)擊登錄")
def test1(self):
print('3. 輸??戶名密碼驗(yàn)證碼1,點(diǎn)擊登錄 ')
def test2(self):
print('3. 輸??戶名密碼驗(yàn)證碼1,點(diǎn)擊登錄 ')
def test3(self):
print('3. 輸??戶名密碼驗(yàn)證碼1,點(diǎn)擊登錄 ')
def tearDown(self) -> None:
print("4.關(guān)閉網(wǎng)頁(yè)")
@classmethod
def tearDownClass(cls) -> None:
print("5.關(guān)閉瀏覽器")
使用代碼自動(dòng)的判斷預(yù)期結(jié)果和實(shí)際結(jié)果是否相符
assertEqual(預(yù)期結(jié)果,實(shí)際結(jié)果)
- 判斷預(yù)期結(jié)果和實(shí)際結(jié)果是否相等,如果相等, 用例通過(guò),如果不相等,拋出異常, 用例不通過(guò)
assertIn(預(yù)期結(jié)果,實(shí)際結(jié)果)
- 判斷預(yù)期結(jié)果是否包含在 實(shí)際結(jié)果中, 如果存在,用例通過(guò), 如果不存在,拋出異常,用例不通過(guò)
import unittest
class TestAssert(unittest.TestCase):
def test_equal_1(self):
self.assertEqual(10, 10) # 用例通過(guò)
def test_assert_2(self):
self.assertEqual(10, 11) # 用例不通過(guò)
def test_in(self):
# self.assertIn('admin', '歡迎 admin 登錄') # 包含 通過(guò)
# self.assertIn('admin', '歡迎 adminnnnnnnn 登錄') # 包含 通過(guò)
# self.assertIn('admin', '歡迎 aaaaaadminnnnnnnn 登錄') # 包含 通過(guò)
# self.assertIn('admin', '歡迎 adddddmin 登錄') # 不包含 不通過(guò)
self.assertIn('admin', 'admin') # 包含 通過(guò)
import unittest
from hm_02_assert import TestAssert
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestAssert))
unittest.TextTestRunner().run(suite)
- 通過(guò)參數(shù)的方式來(lái)傳遞數(shù)據(jù),從而實(shí)現(xiàn)數(shù)據(jù)和腳本分離。并且可以實(shí)現(xiàn)用例的重復(fù)執(zhí)行。(在書(shū)寫用例方法的時(shí)候,測(cè)
試數(shù)據(jù)使用變量代替,在執(zhí)行的時(shí)候進(jìn)行據(jù)說(shuō)傳遞)
- unittest 測(cè)試框架,本身不支持參數(shù)化,但是可以通過(guò)安裝unittest擴(kuò)展插 件 parameterized 來(lái)實(shí)現(xiàn)。
1. 導(dǎo)包 from para... import para...
2. 修改測(cè)試方法,將測(cè)試方法中的測(cè)試數(shù)據(jù)使用 變量表示
3. 組織測(cè)試數(shù)據(jù),格式 [(), (), ()], 一個(gè)元組就是一組測(cè)試數(shù)據(jù)
4. 參數(shù)化,在測(cè)試方法上方使用裝飾器 @parameterized.expand(測(cè)試數(shù)據(jù))
5. 運(yùn)行(直接 TestCase 或者 使用 suite 運(yùn)行)
import unittest
from tools import add
from parameterized import parameterized
data = [(1, 1, 2), (1, 2, 3), (2, 3, 5), (4, 5, 9)]
class TestAdd(unittest.TestCase):
@parameterized.expand(data)
def test_add(self, a, b, expect):
print(f'a:{a}, b:{b}, expect:{expect}')
self.assertEqual(expect, add(a, b))
if __name__ == '__main__':
unittest.main()
將測(cè)試數(shù)據(jù) 定義為 json 文件, 讀取 json 文件,完成參數(shù)化
json 文件
[
[1, 1, 2],
[1, 2, 3],
[2, 3, 5],
[4, 5, 9],
[10, 20, 30]
]
讀取 json 文件
import json
def build_add_data():
with open('add_data.json') as f:
data = json.load(f) # [[], [], []] ---> [(), ()]
return data
代碼文件
import unittest
from read_data import build_add_data
from tools import add
from parameterized import parameterized
data = [(1, 1, 2), (1, 2, 3), (2, 3, 5), (4, 5, 9)]
class TestAdd(unittest.TestCase):
@parameterized.expand(build_add_data())
def test_add(self, a, b, expect):
print(f'a:{a}, b:{b}, expect:{expect}')
self.assertEqual(expect, add(a, b))
if __name__ == '__main__':
unittest.main()
使用第三方的報(bào)告模版,生成報(bào)告 HTMLTestReport, 本質(zhì)是 TestRunner
- 安裝
pip install -i https://pypi.douban.com/simple/ HTMLTestReport
- 使用
1. 導(dǎo)包 unittest、HTMLTestReport
2. 組裝用例(套件, loader )
3. 使用 HTMLTestReport 中的 runner 執(zhí)行套件
4. 查看報(bào)告
import unittest
from htmltestreport import HTMLTestReport
from hm_04_pa1 import TestAdd
# 套件
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestAdd))
# 運(yùn)行對(duì)象
# runner = HTMLTestReport(報(bào)告的文件路徑后綴.html, 報(bào)告的標(biāo)題, 其他的描述信息)
runner = HTMLTestReport('test_add_report.html', '加法用例測(cè)試報(bào)告', 'xxx')
runner.run(suite)
將來(lái)的項(xiàng)目是分目錄書(shū)寫的, 使用相對(duì)路徑,可能會(huì)出現(xiàn)找不到文件的情況,此時(shí)需要使用 絕對(duì)路徑
方法:
1. 在項(xiàng)目的根目錄,創(chuàng)建一個(gè) Python 文件(app.py 或者 config.py)
2. 在這個(gè)文件中 獲取項(xiàng)目的目錄,在其他代碼中使用 路徑拼接完成絕對(duì)路徑的書(shū)寫

import os
# __file__ 特殊的變量,表示當(dāng)前代碼文件名
# path1 = os.path.abspath(__file__)
# print(path1)
# path2 = os.path.dirname(path1)
# print(path2)
# BASE_DIR = os.path.dirname(os.path.abspath(__file__))
BASE_DIR = os.path.dirname(__file__)
if __name__ == '__main__':
print(BASE_DIR)
1, 對(duì)登錄函數(shù)進(jìn)行測(cè)試, 登錄函數(shù) 定義在 tools.py 中
2, 在 case 目錄中書(shū)寫用例對(duì)login 函數(shù)進(jìn)行測(cè)試, 使用斷言
3, 將 login 函數(shù)的測(cè)試數(shù)據(jù)定義在 json 文件中,完成參數(shù)化, data 目錄中
4, 生成測(cè)試報(bào)告 report 目錄中
#測(cè)試數(shù)據(jù)的json文件
#login_data.json
[
{
"desc": "正確的用戶名和密碼",
"username": "admin",
"password": "123456",
"expect": "登錄成功"
},
{
"desc": "錯(cuò)誤的用戶名",
"username": "root",
"password": "123456",
"expect": "登錄失敗"
},
{
"desc": "錯(cuò)誤的密碼",
"username": "admin",
"password": "123123",
"expect": "登錄失敗"
},
{
"desc": "錯(cuò)誤的用戶名和密碼",
"username": "root",
"password": "123123",
"expect": "登錄失敗"
}
]
#讀取測(cè)試數(shù)據(jù)
#read_data.py
def build_login_data():
with open(BASE_DIR + '/data/login_data.json', encoding='utf-8') as f:
data_list = json.load(f) # [{}, {}] ---> [()]
new_list = []
for data in data_list:
# 字典中的 desc 不需要
username = data.get('username')
password = data.get('password')
expect = data.get('expect')
new_list.append((username, password, expect))
return new_list
#Suite 報(bào)告代碼
#login.py
import unittest
from app import BASE_DIR
from case.test_login import TestLogin
from htmltestreport import HTMLTestReport
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestLogin))
runner = HTMLTestReport(BASE_DIR + '/report/login_report.html', '登錄測(cè)試報(bào)告', 'V1.0')
runner.run(suite)
跳過(guò):對(duì)于一些未完成的或者不滿足測(cè)試條件的測(cè)試函數(shù)和測(cè)試類,可以跳過(guò)執(zhí)行(簡(jiǎn)單來(lái)說(shuō), 不想執(zhí)行的測(cè)試方法,可以設(shè)置為跳過(guò))
- 直接將測(cè)試函數(shù)標(biāo)記成跳過(guò)
@unittest.skip('跳過(guò)的原因')
- 根據(jù)條件判斷測(cè)試函數(shù)是否跳過(guò)
@unittest.skipIf(判斷條件, reason='原因') # 判斷條件為 True, 執(zhí)行跳過(guò)
import unittest
version = 29
class TestSkip(unittest.TestCase):
@unittest.skip('沒(méi)什么原因,就是不想執(zhí)行')
def test_1(self):
print('方法一')
@unittest.skipIf(version >= 30, '版本號(hào)大于等于 30, 測(cè)方法不用執(zhí)行')
def test_2(self):
print('方法二')
def test_3(self):
print('方法三')
if __name__ == '__main__':
unittest.main()
什么是框架?
1. 框架英文單詞framework
2. 為解決一類事情的功能集合
是Python自帶的一個(gè)單元測(cè)試框架
- ?帶的, 可以直接使?, 不需要單外安裝
- 測(cè)試?員 ?來(lái)做?動(dòng)化測(cè)試, 作為?動(dòng)化測(cè)試的執(zhí)?框架, 即 管理和執(zhí)??例的
1. 能夠組織多個(gè)用例去執(zhí)行
2. 提供豐富的斷言方法
3. 能夠生成測(cè)試報(bào)告
1. TestCase 測(cè)試?例, 這個(gè)測(cè)試?例是 unittest 的組成部分,作?是 ?來(lái)書(shū)寫真正的?例代碼(腳本)
2. Testsuite 測(cè)試套件, 作?是?來(lái)組裝(打包)TestCase(測(cè)試?例) 的,即 可以將多個(gè)?例腳本?件 組裝到?起
3. TestRunner 測(cè)試執(zhí)?(測(cè)試運(yùn)?), 作? 是?例執(zhí)?TestSuite(測(cè)試套件)的
4. TestLoader 測(cè)試加載, 是對(duì) TestSuite(測(cè)試套件) 功能的補(bǔ)充, 作?是?來(lái)組裝(打包) TestCase(測(cè)試?例) 的
5. Fixture 測(cè)試夾具, 是?種代碼結(jié)構(gòu), 書(shū)寫 前置?法(執(zhí)??例之前的?法)代碼 和后置?法(執(zhí)??例之后的?法) 代碼 ,即 ?例執(zhí)?順序 前置 ---> ?例 ---> 后置
書(shū)寫真正的?例代碼(腳本)
單獨(dú)?個(gè)測(cè)試?例 也是可以執(zhí)?
步驟:
1. 導(dǎo)包 unittest
2. 定義測(cè)試類, 需要繼承 unittest.TestCase 類, 習(xí)慣性類名以 Test 開(kāi)頭
3. 書(shū)寫測(cè)試?法, 必須以 test 開(kāi)頭
4. 執(zhí)?
注意事項(xiàng)
1. 代碼?件名字 要滿?標(biāo)識(shí)符的規(guī)則
2. 代碼?件名 不要使?中?
代碼
# 1. 導(dǎo)包
import unittest
# 2. 定義測(cè)試類:新建 測(cè)試類 ;只要繼承unittest.TestCase類,就是測(cè)試類
class TestDemo(unittest.TestCase):
# 3. 定義測(cè)試方法:測(cè)試方法名稱命名必須以test開(kāi)頭
def test_01(self):
print("測(cè)試一")
def test_02(self):
print("測(cè)試二")
# 4.執(zhí)行
# 4.1 在類名或者?法名后邊右鍵運(yùn)?
# 4.1.1 在類名后邊, 執(zhí)?類中的所有的測(cè)試?法
# 4.1.2 在?法名后邊, 只執(zhí)?當(dāng)前的測(cè)試?法
將多條?例腳本集合在?起,就是套件, 即?來(lái)組裝?例的
步驟
1. 導(dǎo)包 unittest
2. 實(shí)例化套件對(duì)象 unittest.TestSuite()
3. 添加?例?法
?來(lái)執(zhí)?套件對(duì)象
步驟
1. 導(dǎo)包 unittest
2. 實(shí)例化 執(zhí)?對(duì)象 unittest.TextTestRunner()
3. 執(zhí)?對(duì)象執(zhí)? 套件對(duì)象 執(zhí)?對(duì)象.run(套件對(duì)象)
1. 導(dǎo)包 unittest
2. 實(shí)例化套件對(duì)象 unittest.TestSuite()
3. 添加?例?法
- 3.1 套件對(duì)象.addTest(測(cè)試類名('測(cè)試?法名'))
4. 實(shí)例化 執(zhí)?對(duì)象 unittest.TextTestRunner()
5. 執(zhí)?對(duì)象執(zhí)? 套件對(duì)象 執(zhí)?對(duì)象.run(套件對(duì)象)
# 1. 導(dǎo)包 unittest
import unittest
from TestCase import TestDemo
from TestCase_01 import TestDemo01
# 2. 實(shí)例化套件對(duì)象 unittest.TestSuite()
suite = unittest.TestSuite()
# 3. 添加?例?法
# - 3.1 套件對(duì)象.addTest(測(cè)試類名('測(cè)試?法名'))
suite.addTest(TestDemo('test_01'))
suite.addTest(TestDemo('test_02'))
suite.addTest(TestDemo01('test_03'))
suite.addTest(TestDemo01('test_04'))
# - 3.2套件對(duì)象.addTest(unittest.makeSuite(測(cè)試類名)) # 在
不同的 Python 版本中,可能沒(méi)有提示
suite.addTest(unittest.makeSuite(TestDemo1))
suite.addTest(unittest.makeSuite(TestDemo2))
# - 3.3
suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestDemo1))
suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestDemo2))
# 4. 實(shí)例化 執(zhí)?對(duì)象 unittest.TextTestRunner()
runner = unittest.TextTestRunner()
# 5. 執(zhí)?對(duì)象執(zhí)? 套件對(duì)象 執(zhí)?對(duì)象.run(套件對(duì)象)
runner.run(suite)

作?和 TestSuite 作??樣,組裝?例代碼, 同樣也需要使?TextTestRunner() 去執(zhí)?
需要使用10 個(gè)?例腳本 makeSuite()
使用unittest.TestLoader,通過(guò)該類下面的discover()方法自動(dòng)搜索指定目錄下指定開(kāi)頭的.py文件,并將查找到的測(cè)試用例組裝到測(cè)試套件。
步驟
1. 導(dǎo)包 unittest
2. 實(shí)例化加載對(duì)象并加載?例 ---> 得到的是 套件對(duì)象(最終返回的還是測(cè)試套件對(duì)象,運(yùn)行測(cè)試套件還需要使用TestRunner)
3. 實(shí)例化執(zhí)?對(duì)象并執(zhí)?
import unittest
# 實(shí)例化加載對(duì)象并加載?例,得到套件對(duì)象
# suite = unittest.TestLoader().discover('?例所在的?錄', '?例代碼?件名*.py')
suite = unittest.TestLoader().discover("./", "hm_02*.py")
# runner=unittest.TextTestRunner()
# runner.run(suite)
unittest.TextTestRunner().run(suite)
1. 創(chuàng)建?個(gè)?錄 case, 作?就是?來(lái)存放?例腳本,
2. 在這個(gè)?錄中創(chuàng)建 5 個(gè)?例代碼?件 , test_case1.py
...
3. 使? TestLoader 去執(zhí)??例
-----
將來(lái)的代碼 ?例都是單獨(dú)的?錄 中存放的
test_項(xiàng)?_模塊_功能.py
import unittest
suite = unittest.TestLoader().discover('case', 'test_case*.py')
unittest.TextTestRunner().run(suite)
1. 定義?個(gè) tools 模塊, 在這個(gè)模塊中 定義 add 的?法,可
以對(duì)兩個(gè)數(shù)字求和,返回求和結(jié)果
2. 書(shū)寫?例, 對(duì) add() 函數(shù)進(jìn)?測(cè)試
1, 1, 2
1, 2, 3
3, 4, 7
4, 5, 9
-----
之前的測(cè)試?法,直接?個(gè) print
這個(gè)案例中的 測(cè)試?法,調(diào)? add 函數(shù), 使? if 判斷,來(lái)判斷
預(yù)期結(jié)果和實(shí)際結(jié)果是否相符
預(yù)期結(jié)果 2 3 7 9
實(shí)際結(jié)果 調(diào)? add()
import unittest
from tools import add
class testadd(unittest.TestCase):
def test1(self):
"""1,1,2"""
print(f"測(cè)試數(shù)據(jù)為:{1},{1},{2}")
if 2==add(1,1):
print(f"測(cè)試數(shù)據(jù)為:{1},{1},{2}通過(guò)")
else:
print(f"測(cè)試數(shù)據(jù)為:{1},{1},{2}不通過(guò)")
def test2(self):
"""1,2,3"""
print(f"測(cè)試數(shù)據(jù)為:{1},{2},{3}")
if 3==add(1,2):
print(f"測(cè)試數(shù)據(jù)為:{1},{2},{3}通過(guò)")
else:
print(f"測(cè)試數(shù)據(jù)為:{1},{2},{3}不通過(guò)")
def test3(self):
"""3,4,7"""
print(f"測(cè)試數(shù)據(jù)為:{3},{4},{7}")
if 3==add(1,2):
print(f"測(cè)試數(shù)據(jù)為:{3},{4},{7}通過(guò)")
else:
print(f"測(cè)試數(shù)據(jù)為:{3},{4},{7}不通過(guò)")
def test4(self):
"""4,5,9"""
print(f"測(cè)試數(shù)據(jù)為:{4},{5},{9}")
if 3==add(1,2):
print(f"測(cè)試數(shù)據(jù)為:{4},{5},{9}通過(guò)")
else:
print(f"測(cè)試數(shù)據(jù)為:{4},{5},{9}不通過(guò)")
代碼結(jié)構(gòu), 在?例執(zhí)?前后會(huì)?動(dòng)執(zhí)?的代碼結(jié)構(gòu)
tpshop 登錄
1. 打開(kāi)瀏覽器 (?次)
2. 打開(kāi)??,點(diǎn)擊登錄 (每次)
3. 輸??戶名密碼驗(yàn)證碼1,點(diǎn)擊登錄 (每次, 測(cè)試?法)
4. 關(guān)閉?? (每次)
2. 打開(kāi)??,點(diǎn)擊登錄 (每次)
3. 輸??戶名密碼驗(yàn)證碼2,點(diǎn)擊登錄 (每次, 測(cè)試?法)
4. 關(guān)閉?? (每次)
2. 打開(kāi)??,點(diǎn)擊登錄 (每次)
3. 輸??戶名密碼驗(yàn)證碼3,點(diǎn)擊登錄 (每次, 測(cè)試?法)
4. 關(guān)閉?? (每次)
5. 關(guān)閉瀏覽器 (?次)
在每個(gè)?例執(zhí)?前后都會(huì)?動(dòng)調(diào)?, ?法名是固定的
def setUp(self): # 前置
# 每個(gè)?例執(zhí)?之前都會(huì)?動(dòng)調(diào)?
pass
def tearDown(self): # 后置
# 每個(gè)?例執(zhí)?之后 都會(huì)?動(dòng)調(diào)?
pass
# ?法前置 ?例 ?法后置
# ?法前置 ?例 ?法后置
在類中所有的測(cè)試?法執(zhí)?前后 會(huì)?動(dòng)執(zhí)?的代碼, 只執(zhí)??次
# 類級(jí)別的 Fixture 需要寫作類?法
@classmethod
def setUpClass(cls): # 類前置
pass
@classmethod
def tearDownClass(cls): # 后置
pass
# 類前置 ?法前置 ?例 ?法后置 ?法前置 ?例 ?法后置類后置
模塊, 就是代碼?件
模塊級(jí)別 在這個(gè)代碼?件執(zhí)?前后執(zhí)??次
# 在類外部定義函數(shù)
def setUpModule():
pass
def tearDownModule():
pass
實(shí)現(xiàn):
tpshop 登錄
1. 打開(kāi)瀏覽器 (?次)
2. 打開(kāi)??,點(diǎn)擊登錄 (每次)
3. 輸??戶名密碼驗(yàn)證碼1,點(diǎn)擊登錄 (每次, 測(cè)試?法)
4. 關(guān)閉?? (每次)
2. 打開(kāi)??,點(diǎn)擊登錄 (每次)
3. 輸??戶名密碼驗(yàn)證碼2,點(diǎn)擊登錄 (每次, 測(cè)試?法)
4. 關(guān)閉?? (每次)
2. 打開(kāi)??,點(diǎn)擊登錄 (每次)
3. 輸??戶名密碼驗(yàn)證碼3,點(diǎn)擊登錄 (每次, 測(cè)試?法)
4. 關(guān)閉?? (每次)
5. 關(guān)閉瀏覽器 (?次)
import unittest
class TestLogin(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print("1.打開(kāi)瀏覽器")
def setUp(self) -> None:
print("2.打開(kāi)??, 點(diǎn)擊登錄")
def test1(self):
print('3. 輸??戶名密碼驗(yàn)證碼1,點(diǎn)擊登錄 ')
def test2(self):
print('3. 輸??戶名密碼驗(yàn)證碼1,點(diǎn)擊登錄 ')
def test3(self):
print('3. 輸??戶名密碼驗(yàn)證碼1,點(diǎn)擊登錄 ')
def tearDown(self) -> None:
print("4.關(guān)閉網(wǎng)頁(yè)")
@classmethod
def tearDownClass(cls) -> None:
print("5.關(guān)閉瀏覽器")
使用代碼自動(dòng)的判斷預(yù)期結(jié)果和實(shí)際結(jié)果是否相符
assertEqual(預(yù)期結(jié)果,實(shí)際結(jié)果)
- 判斷預(yù)期結(jié)果和實(shí)際結(jié)果是否相等,如果相等, 用例通過(guò),如果不相等,拋出異常, 用例不通過(guò)
assertIn(預(yù)期結(jié)果,實(shí)際結(jié)果)
- 判斷預(yù)期結(jié)果是否包含在 實(shí)際結(jié)果中, 如果存在,用例通過(guò), 如果不存在,拋出異常,用例不通過(guò)
import unittest
class TestAssert(unittest.TestCase):
def test_equal_1(self):
self.assertEqual(10, 10) # 用例通過(guò)
def test_assert_2(self):
self.assertEqual(10, 11) # 用例不通過(guò)
def test_in(self):
# self.assertIn('admin', '歡迎 admin 登錄') # 包含 通過(guò)
# self.assertIn('admin', '歡迎 adminnnnnnnn 登錄') # 包含 通過(guò)
# self.assertIn('admin', '歡迎 aaaaaadminnnnnnnn 登錄') # 包含 通過(guò)
# self.assertIn('admin', '歡迎 adddddmin 登錄') # 不包含 不通過(guò)
self.assertIn('admin', 'admin') # 包含 通過(guò)
import unittest
from hm_02_assert import TestAssert
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestAssert))
unittest.TextTestRunner().run(suite)
- 通過(guò)參數(shù)的方式來(lái)傳遞數(shù)據(jù),從而實(shí)現(xiàn)數(shù)據(jù)和腳本分離。并且可以實(shí)現(xiàn)用例的重復(fù)執(zhí)行。(在書(shū)寫用例方法的時(shí)候,測(cè)
試數(shù)據(jù)使用變量代替,在執(zhí)行的時(shí)候進(jìn)行據(jù)說(shuō)傳遞)
- unittest 測(cè)試框架,本身不支持參數(shù)化,但是可以通過(guò)安裝unittest擴(kuò)展插 件 parameterized 來(lái)實(shí)現(xiàn)。
1. 導(dǎo)包 from para... import para...
2. 修改測(cè)試方法,將測(cè)試方法中的測(cè)試數(shù)據(jù)使用 變量表示
3. 組織測(cè)試數(shù)據(jù),格式 [(), (), ()], 一個(gè)元組就是一組測(cè)試數(shù)據(jù)
4. 參數(shù)化,在測(cè)試方法上方使用裝飾器 @parameterized.expand(測(cè)試數(shù)據(jù))
5. 運(yùn)行(直接 TestCase 或者 使用 suite 運(yùn)行)
import unittest
from tools import add
from parameterized import parameterized
data = [(1, 1, 2), (1, 2, 3), (2, 3, 5), (4, 5, 9)]
class TestAdd(unittest.TestCase):
@parameterized.expand(data)
def test_add(self, a, b, expect):
print(f'a:{a}, b:{b}, expect:{expect}')
self.assertEqual(expect, add(a, b))
if __name__ == '__main__':
unittest.main()
將測(cè)試數(shù)據(jù) 定義為 json 文件, 讀取 json 文件,完成參數(shù)化
json 文件
[
[1, 1, 2],
[1, 2, 3],
[2, 3, 5],
[4, 5, 9],
[10, 20, 30]
]
讀取 json 文件
import json
def build_add_data():
with open('add_data.json') as f:
data = json.load(f) # [[], [], []] ---> [(), ()]
return data
代碼文件
import unittest
from read_data import build_add_data
from tools import add
from parameterized import parameterized
data = [(1, 1, 2), (1, 2, 3), (2, 3, 5), (4, 5, 9)]
class TestAdd(unittest.TestCase):
@parameterized.expand(build_add_data())
def test_add(self, a, b, expect):
print(f'a:{a}, b:{b}, expect:{expect}')
self.assertEqual(expect, add(a, b))
if __name__ == '__main__':
unittest.main()
使用第三方的報(bào)告模版,生成報(bào)告 HTMLTestReport, 本質(zhì)是 TestRunner
- 安裝
pip install -i https://pypi.douban.com/simple/ HTMLTestReport
- 使用
1. 導(dǎo)包 unittest、HTMLTestReport
2. 組裝用例(套件, loader )
3. 使用 HTMLTestReport 中的 runner 執(zhí)行套件
4. 查看報(bào)告
import unittest
from htmltestreport import HTMLTestReport
from hm_04_pa1 import TestAdd
# 套件
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestAdd))
# 運(yùn)行對(duì)象
# runner = HTMLTestReport(報(bào)告的文件路徑后綴.html, 報(bào)告的標(biāo)題, 其他的描述信息)
runner = HTMLTestReport('test_add_report.html', '加法用例測(cè)試報(bào)告', 'xxx')
runner.run(suite)
將來(lái)的項(xiàng)目是分目錄書(shū)寫的, 使用相對(duì)路徑,可能會(huì)出現(xiàn)找不到文件的情況,此時(shí)需要使用 絕對(duì)路徑
方法:
1. 在項(xiàng)目的根目錄,創(chuàng)建一個(gè) Python 文件(app.py 或者 config.py)
2. 在這個(gè)文件中 獲取項(xiàng)目的目錄,在其他代碼中使用 路徑拼接完成絕對(duì)路徑的書(shū)寫

import os
# __file__ 特殊的變量,表示當(dāng)前代碼文件名
# path1 = os.path.abspath(__file__)
# print(path1)
# path2 = os.path.dirname(path1)
# print(path2)
# BASE_DIR = os.path.dirname(os.path.abspath(__file__))
BASE_DIR = os.path.dirname(__file__)
if __name__ == '__main__':
print(BASE_DIR)
1, 對(duì)登錄函數(shù)進(jìn)行測(cè)試, 登錄函數(shù) 定義在 tools.py 中
2, 在 case 目錄中書(shū)寫用例對(duì)login 函數(shù)進(jìn)行測(cè)試, 使用斷言
3, 將 login 函數(shù)的測(cè)試數(shù)據(jù)定義在 json 文件中,完成參數(shù)化, data 目錄中
4, 生成測(cè)試報(bào)告 report 目錄中
#測(cè)試數(shù)據(jù)的json文件
#login_data.json
[
{
"desc": "正確的用戶名和密碼",
"username": "admin",
"password": "123456",
"expect": "登錄成功"
},
{
"desc": "錯(cuò)誤的用戶名",
"username": "root",
"password": "123456",
"expect": "登錄失敗"
},
{
"desc": "錯(cuò)誤的密碼",
"username": "admin",
"password": "123123",
"expect": "登錄失敗"
},
{
"desc": "錯(cuò)誤的用戶名和密碼",
"username": "root",
"password": "123123",
"expect": "登錄失敗"
}
]
#讀取測(cè)試數(shù)據(jù)
#read_data.py
def build_login_data():
with open(BASE_DIR + '/data/login_data.json', encoding='utf-8') as f:
data_list = json.load(f) # [{}, {}] ---> [()]
new_list = []
for data in data_list:
# 字典中的 desc 不需要
username = data.get('username')
password = data.get('password')
expect = data.get('expect')
new_list.append((username, password, expect))
return new_list
#Suite 報(bào)告代碼
#login.py
import unittest
from app import BASE_DIR
from case.test_login import TestLogin
from htmltestreport import HTMLTestReport
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestLogin))
runner = HTMLTestReport(BASE_DIR + '/report/login_report.html', '登錄測(cè)試報(bào)告', 'V1.0')
runner.run(suite)
跳過(guò):對(duì)于一些未完成的或者不滿足測(cè)試條件的測(cè)試函數(shù)和測(cè)試類,可以跳過(guò)執(zhí)行(簡(jiǎn)單來(lái)說(shuō), 不想執(zhí)行的測(cè)試方法,可以設(shè)置為跳過(guò))
- 直接將測(cè)試函數(shù)標(biāo)記成跳過(guò)
@unittest.skip('跳過(guò)的原因')
- 根據(jù)條件判斷測(cè)試函數(shù)是否跳過(guò)
@unittest.skipIf(判斷條件, reason='原因') # 判斷條件為 True, 執(zhí)行跳過(guò)
import unittest
version = 29
class TestSkip(unittest.TestCase):
@unittest.skip('沒(méi)什么原因,就是不想執(zhí)行')
def test_1(self):
print('方法一')
@unittest.skipIf(version >= 30, '版本號(hào)大于等于 30, 測(cè)方法不用執(zhí)行')
def test_2(self):
print('方法二')
def test_3(self):
print('方法三')
if __name__ == '__main__':
unittest.main()