Python自動化之UnitTest框架實(shí)戰(zhàn)記錄
1 UnitTest 基本用法
UnitTest 框架是 Python 自帶的一個(gè)作為單元測試的測試框,相當(dāng)于 Java中的 JUnit,隨著自動化技術(shù)的成熟,UnitTest 成為了測試框架第一選擇,可以完整的結(jié)合 Selenium、Requests 來實(shí)現(xiàn) Ul 和接口的自動化,由 UnitTest 再衍生出 PyTest,PyTest 可以完美結(jié)合 UnitTest 來實(shí)現(xiàn)自動化。
基本應(yīng)用:
1、環(huán)境搭建,Python 中已經(jīng)直接加載了 UnitTest 框架,無須額外安裝
2、四大組件:
a. test fixture:setUp(前置條件)、tearDown(后置條件),用于初始化測試用例及清理和釋放資源
b. test case:測試用例,通過集成 unttest.TestCase,來實(shí)現(xiàn)用例的繼承,在 Unitfest 中,測試用例都是通過 test 來識別的,測試用例命名 test_XXX
c. test suite:測試套件,也稱之為測試用例集
d. test runner:運(yùn)行器,一般通過 runner 來調(diào)用 suite 去執(zhí)行測試
UnitTest 運(yùn)行機(jī)制:通過在 main 函數(shù)中,調(diào)用 unitest.main() 運(yùn)行所有內(nèi)容
1.1 UnitTest 初體驗(yàn)
本節(jié)知識:1. 對 UnitTest 有直觀感受
講了這么多,也完全不明白,沒關(guān)系,通過實(shí)例先有個(gè)直觀的了解,UnitTest 是個(gè)什么東西
import unittest
# 通過繼承 unittest。TestCase 來實(shí)現(xiàn)用例
class forTest(unittest.TestCase):
# 類的初始化
@classmethod
def setUpClass(cls) -> None:
print('class')
# 類的釋放
@classmethod
def tearDownClass(cls) -> None:
print('tclass')
# 測試用例初始化
def setUp(self) -> None:
print("setUp")
# 測試用例釋放
def tearDown(self) -> None:
print("teadDown")
# 測試用例
def test_a(self):
print("a")
# 測試用例
def test_b(self):
print("b")
# 函數(shù)
def add(self, a, b):
return a + b
# 測試用例
def test_c(self):
c = self.add(1, 3)
print('c =', c)
if __name__ == "__main__":
unittest.main(verbosity=2) # 參數(shù) verbosity=2 的目的是為了讓打印的信息更加完整,也可以不要
對上面的程序進(jìn)行講解:
1、類的初始化與釋放
def setUpClass(cls) -> None 表示類的初始化,在執(zhí)行測試用例之前執(zhí)行,只執(zhí)行一次,函數(shù)參數(shù)為 cls 表示這是一個(gè)類方法
def tearDownClass(cls) -> None 表示類的釋放,在執(zhí)行測試用例之后執(zhí)行,只執(zhí)行一次
2、測試用例的初始化與釋放
def setUp(self) -> None 用于測試用例的初始化,在每個(gè)測試用例之前都會執(zhí)行,有多少個(gè)測試用例,就會執(zhí)行多少次
def tearDown(self) -> None 用于測試用例釋放,在每個(gè)測試用例執(zhí)行之后執(zhí)行,有多少個(gè)測試用例,就會執(zhí)行多少次
注意:方法 setUpClass,tearDownClass,setUp,def tearDown 的方法名是固定的,不能改動,不然框架無法識別
3、測試用例的定義
測試用例的命名規(guī)則為 test_xxx,這樣測試用例就會自動執(zhí)行
注意:只有測試用例才會被執(zhí)行,不以test_xxx 命名的函數(shù)是方法,方法是不能被執(zhí)行的
4、執(zhí)行測試用例
通過在 main 函數(shù)中,調(diào)用 unitest.main() 運(yùn)行所有內(nèi)容
運(yùn)行結(jié)果如下:
- 類的初始化方法 setUpClass(cls) 在所有的測試用例之前執(zhí)行,類的釋放函數(shù) tearDownClass(cls) 在所有的測試用例之后執(zhí)行
- 測試用例的初始化在每個(gè)測試用例之前都會執(zhí)行,測試用例的釋放在每個(gè)測試用例之后都會執(zhí)行
- test_a(self) 和 test_b(self) 是測試用例,運(yùn)行時(shí)被自動執(zhí)行,add(self, a, b) 是函數(shù),不會被自動執(zhí)行,test_c(self) 是測試用例,調(diào)用了 add 函數(shù),這樣就可以執(zhí)行 add 函數(shù)了。
class setUp a teadDown setUp b teadDown setUp c = 4 teadDown tclass
相信有了上面的例子,已經(jīng)對UnitTest 有了一個(gè)初步的印象。
下面我們進(jìn)行一個(gè)實(shí)戰(zhàn)操作
1.2 UnitTest 自動化實(shí)現(xiàn)實(shí)戰(zhàn)
本節(jié)知識:1. 自動化測試減少冗余,便于維護(hù),2. ddt數(shù)據(jù)驅(qū)動
1、自動化測試減少冗余,便于維護(hù)
有了類的初始化與釋放,測試用例的初始化與釋放,我們可以將多個(gè)測試用例中相同的代碼提取出來,減少自動化測試冗余,這樣便于維護(hù)
下面看這樣一個(gè)例子,我們打開谷歌瀏覽器,輸入百度網(wǎng)址并進(jìn)行搜索,搜索后關(guān)閉瀏覽器
#coding=utf-8
import unittest
from selenium import webdriver
import time
class forTest(unittest.TestCase):
# 測試用例初始化
# 打開谷歌瀏覽器,并進(jìn)入百度
def setUp(self) -> None:
self.driver = webdriver.Chrome()
self.driver.get('http://www.baidu.com')
# 測試用例釋放
# 等待 3s,關(guān)閉瀏覽器
def tearDown(self) -> None:
time.sleep(3)
self.driver.quit()
# 輸入‘戰(zhàn)狼',并點(diǎn)擊搜索
def test_1(self):
pass
self.driver.find_element_by_id('kw').send_keys('戰(zhàn)狼')
self.driver.find_element_by_id('su').click()
# 輸入‘紅海行動',并點(diǎn)擊搜索
def test_2(self):
pass
self.driver.find_element_by_id('kw').send_keys('紅海行動')
self.driver.find_element_by_id('su').click()
if __name__ == '__main__':
unittest.main()
上面的案例中,我們將打開谷歌瀏覽器,進(jìn)入百度,放在 setUp 中,完成每個(gè)測試用例之前的初始化,瀏覽器的關(guān)閉放在tearDown 中,完成測試用例的釋放
2 UnitTest 結(jié)合 DDT(data-driver tests) 自動化
2.1 ddt 中的 data 與 unpack
在實(shí)際測試中,單個(gè)測試是需要用多種不同的條件(測試數(shù)據(jù))對其進(jìn)行測試的。
ddt 中最基本的應(yīng)用;在 class 前定義 @ddt,用于表示要使用 ddt 了,再基于實(shí)際的應(yīng)用。選擇對應(yīng)的裝飾器來使用即可,說白了,就是一個(gè)裝飾器
- data 用于設(shè)定參數(shù)
- unpack 用于解析參數(shù)
直接看例子比較直觀
#coding=utf-8
import unittest
from ddt import ddt
from ddt import data # 導(dǎo)入data
# 類之前定義裝飾器,表示在類中要使用ddt了
@ddt
class MyTestCase(unittest.TestCase):
def setUp(self) -> None:
print('{:=^20}'.format("測試開始"))
def tearDown(self) -> None:
print("{:=^20}".format('測試結(jié)束'))
# data用于設(shè)定參數(shù)
@data('戰(zhàn)狼', '哪吒', '流浪地球', '復(fù)仇者聯(lián)盟')
def test_1(self, txt):
print(txt)
if __name__ == '__main__':
unittest.main(verbosity=2)
運(yùn)行結(jié)果:
========測試開始========
戰(zhàn)狼
========測試結(jié)束========
========測試開始========
哪吒
========測試結(jié)束========
========測試開始========
流浪地球
========測試結(jié)束========
========測試開始========
復(fù)仇者聯(lián)盟
========測試結(jié)束========
可以看到測試用例 def test_1(self, txt) 被執(zhí)行了四次,data 用于設(shè)定參數(shù),將參數(shù)依次放入測試用例中進(jìn)行測試。
我們改變一下設(shè)定的參數(shù),將 data 設(shè)定的參數(shù)改為 ((‘戰(zhàn)狼', ‘哪吒'), (‘流浪地球', ‘復(fù)仇者聯(lián)盟')),再進(jìn)行測試,如下所示
#coding=utf-8
import unittest
from ddt import ddt
from ddt import data
# 類之前定義裝飾器,表示在類中要使用ddt了
@ddt
class MyTestCase(unittest.TestCase):
def setUp(self) -> None:
print('{:=^20}'.format("測試開始"))
def tearDown(self) -> None:
print("{:=^20}".format('測試結(jié)束'))
# data 用于設(shè)定參數(shù),將包 ('戰(zhàn)狼', '哪吒') 作為一個(gè)整體賦值給 txt
@data(('戰(zhàn)狼', '哪吒'), ('流浪地球', '復(fù)仇者聯(lián)盟'))
def test_1(self, txt):
print(txt)
if __name__ == '__main__':
unittest.main(verbosity=2)
運(yùn)行結(jié)果如下:
========測試開始========
('戰(zhàn)狼', '哪吒')
========測試結(jié)束========
========測試開始========
('流浪地球', '復(fù)仇者聯(lián)盟')
========測試結(jié)束========
可以看到,傳入?yún)?shù) ((‘戰(zhàn)狼', ‘哪吒'), (‘流浪地球', ‘復(fù)仇者聯(lián)盟')) 時(shí),將包 (‘戰(zhàn)狼', ‘哪吒') 和 (‘流浪地球', ‘復(fù)仇者聯(lián)盟') 作為一個(gè)整體,傳遞給測試用例了,如果我們希望將包里面的數(shù)據(jù)解開,傳遞給測試用例不同的參數(shù),就需要用到 unpack 進(jìn)行解包。
加入解包后的代碼如下所示:
#coding=utf-8
import unittest
from ddt import ddt
from ddt import data
from ddt import unpack # 導(dǎo)入unpack
# 類之前定義裝飾器,表示在類中要使用ddt了
@ddt
class MyTestCase(unittest.TestCase):
def setUp(self) -> None:
print('{:=^20}'.format("測試開始"))
def tearDown(self) -> None:
print("{:=^20}".format('測試結(jié)束'))
@data(('戰(zhàn)狼', '哪吒'), ('流浪地球', '復(fù)仇者聯(lián)盟'))
# 解包,將 ('戰(zhàn)狼', '哪吒') 解包,'戰(zhàn)狼' 賦值給 txt1,'哪吒'賦值給 txt2
@unpack
def test_3(self, txt1, txt2):
print(txt1)
print(txt2)
if __name__ == '__main__':
unittest.main(verbosity=2)
執(zhí)行結(jié)果如下:
========測試開始========
戰(zhàn)狼
哪吒
========測試結(jié)束========
========測試開始========
流浪地球
復(fù)仇者聯(lián)盟
========測試結(jié)束========
可以看到,unpack 對每次傳入的包進(jìn)行解包,例如將 (‘戰(zhàn)狼', ‘哪吒') 解包,‘戰(zhàn)狼' 賦值給 txt1,'哪吒'賦值給 txt2
上面的例子中,我們將輸入的參數(shù)直接固定了,其實(shí)也可以通過文件讀取,讀取結(jié)果決定
#coding=utf-8
import unittest
from ddt import ddt
from ddt import data
from ddt import unpack
def readFile():
params = []
file = open('ddt.txt', 'r', encoding = 'gbk')
for line in file.readlines():
params.append(line.strip('\n').split(','))
return params
# 類之前定義裝飾器,表示在類中要使用ddt了
@ddt
class MyTestCase(unittest.TestCase):
def setUp(self) -> None:
print('{:=^20}'.format("測試開始"))
def tearDown(self) -> None:
print("{:=^20}".format('測試結(jié)束'))
# 從文件中讀取
@data(*readFile())
@unpack
def test_1(self, txt1, txt2):
print(txt1)
print(txt2)
if __name__ == '__main__':
unittest.main(verbosity=2)
ddt.txt 文件中的內(nèi)如下:
戰(zhàn)狼,哪吒
流浪地球,復(fù)仇者聯(lián)盟
運(yùn)行結(jié)果:
函數(shù) readFile 從文件中讀取數(shù)據(jù),unpack 進(jìn)行解包
========測試開始========
戰(zhàn)狼
哪吒
========測試結(jié)束========
========測試開始========
流浪地球
復(fù)仇者聯(lián)盟
========測試結(jié)束========
上面從文件中讀取數(shù)據(jù)時(shí)先讀取文件,再處理讀取的結(jié)果,下面介紹一個(gè)直接操作文件的方法
從 ddt 中導(dǎo)入 file_data,導(dǎo)入 yaml,讀取數(shù)據(jù)的文件類型必須為 .yml 類型的文件。
#coding=utf-8
import unittest
from ddt import ddt
from ddt import data
from ddt import unpack
from ddt import file_data
import yaml
# 類之前定義裝飾器,表示在類中要使用ddt了
@ddt
class MyTestCase(unittest.TestCase):
def setUp(self) -> None:
print('{:=^20}'.format("測試開始"))
def tearDown(self) -> None:
print("{:=^20}".format('測試結(jié)束'))
# 直接的文件讀取,直接操作一個(gè)文件
@file_data('ddt2.yml')
def test_5(self, txt):
print(txt)
if __name__ == '__main__':
unittest.main(verbosity=2)
ddt2.yml 文件內(nèi)容如下:
name: 'skx' info: 'hust'
運(yùn)行結(jié)果:
========測試開始========
skx
========測試結(jié)束========
========測試開始========
hust
========測試結(jié)束========
2.2 ddt 數(shù)據(jù)驅(qū)動
打開瀏覽器進(jìn)入百度查詢的例子中我們發(fā)現(xiàn)除了輸入的參數(shù)不同,test_1(self) 和 test_2(self) 完全相同,這里我們就要通過 data 設(shè)定參數(shù)實(shí)現(xiàn)在一個(gè)測試用例中輸入不同的參數(shù)
#coding=utf-8
import unittest
from selenium import webdriver
import time
from ddt import ddt
from ddt import data
# 在 class 前定義 @ddt,用于表示要使用 ddt 了
@ddt
class forTestTest(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
self.driver.get('http://www.baidu.com')
def tearDown(self):
time.sleep(3)
self.driver.quit()
# data 用于設(shè)定參數(shù)
@data('戰(zhàn)狼', '哪吒', '流浪地球')
def test_1(self, txt):
self.driver.find_element_by_id('kw').send_keys(txt)
self.driver.find_element_by_id('su').click()
if __name__ == "__main__":
unittest.main()
運(yùn)行結(jié)果,谷歌瀏覽器被打開三次,進(jìn)入百度,分別輸入 ‘戰(zhàn)狼', ‘哪吒', ‘流浪地球',每次瀏覽器關(guān)閉之后,才打開下一次,再進(jìn)行搜索
上面的例子中,我們將輸入的參數(shù)直接固定了,其實(shí)也可以通過文件讀取,決定進(jìn)入哪一個(gè) url 和輸入的參數(shù)
#coding=utf-8
import unittest
from selenium import webdriver
import time
from ddt import ddt
from ddt import data
from ddt import unpack
def readFile():
params = []
file = open('forTest3.txt', 'r', encoding = 'gbk')
for line in file.readlines():
params.append(line.strip('\n').split(','))
return params
@ddt
class forTestTest(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
def tearDown(self):
time.sleep(3)
self.driver.quit()
# data 用于設(shè)定參數(shù),表示參數(shù)由 readFile() 函數(shù)的返回值決定
# unpack 用于解析參數(shù),例如將['http://www.baidu.com', '戰(zhàn)狼'] 分別 賦值給 url 和 txt
@data(*readFile())
@unpack
def test_1(self, url, txt):
self.driver.get(url)
self.driver.find_element_by_id('kw').send_keys(txt)
self.driver.find_element_by_id('su').click()
if __name__ == "__main__":
unittest.main()
forTest3.txt 文件中的內(nèi)容如下:
http://www.baidu.com,戰(zhàn)狼 http://www.baidu.com,哪吒
分析:
- readFile() 函數(shù)打開文件,讀取文件的每一行,按逗號 ‘,' 劃分關(guān)鍵字,
- unpack 用于解析參數(shù),ddt 對于數(shù)據(jù)的解析方式為,解析一個(gè),傳參一個(gè),所以函數(shù)中 url 和 txt 的參數(shù)順序不能調(diào)換。
運(yùn)行結(jié)果,谷歌瀏覽器被打開兩次,進(jìn)入百度,分別輸入 ‘戰(zhàn)狼', ‘哪吒',每次瀏覽器關(guān)閉之后,才打開下一次,再進(jìn)行搜索
file_data 是 ddt 中用于讀取 yml 文件的裝飾器
3 yml 文件的使用
這個(gè)插入一個(gè)小插曲,上面提到了 yml 文件,這里就簡單講解一下 yml 文件怎么使用。
從yml 文件中直接讀取數(shù)據(jù)可以生成字典,列表等,yml 文件由一定的格式,我們通過實(shí)例來說明,yml_test.py 從 a.yml 文件中讀取文件并打印出來。
yml_test.py
import yaml
file = open('a.yml', encoding = 'utf-8')
res = yaml.load(file, Loader = yaml.FullLoader)
print(res)
a.yml 文件中的內(nèi)容如下所示,冒號代表字典,字典結(jié)構(gòu)可以嵌套,也可以生成列表,具體格式參考下面的 a.yml 文件。
a.yml
name: 'skx' age: 18 data: a: 1 b: 2 c: 3 d: 4 list: - a - b - c - d
打印的結(jié)果如下所示,生成四個(gè)字典元素,第三個(gè)字典元素為嵌套字典結(jié)構(gòu),第四個(gè)字典對應(yīng)的 value 為列表。
{'name': 'skx', 'age': 18, 'data': {'a': 1, 'b': 2, 'c': 3, 'd': 4}, 'list': ['a', 'b', 'c', 'd']}
如果將 a.yml 文件中的數(shù)據(jù)改為如下結(jié)構(gòu),則生成一個(gè)純列表,打印的結(jié)果如下所示。
a.yml
- a - b - c - d
['a', 'b', 'c', 'd']
有了 yml 文件,我們就可以將測試數(shù)據(jù)放到 yml 文件中,從文件中獲取參數(shù),傳入測試函數(shù),完成測試。還是通過例子來講解,yml_test2.yml 中是一個(gè)列表,每個(gè)列表元素是一個(gè)字典,字典中有兩個(gè)元素,name 和 age,使用 file_data 直接可以將 yml_test2.yml 傳入測試用例中。
read_yml2.py
#coding=utf-8
import unittest
from ddt import ddt
from ddt import file_data
import yaml
# 類之前定義裝飾器,表示在類中要使用ddt了
@ddt
class MyTestCase(unittest.TestCase):
def setUp(self) -> None:
print('{:=^20}'.format("測試開始"))
def tearDown(self) -> None:
print("{:=^20}".format('測試結(jié)束'))
@file_data('read_yml2_data.yml')
def test_yam(self, **kwargs):
# 獲取參數(shù)中key 為 name 的 value
print(kwargs["name"])
# 獲取為 text 的 value
print(kwargs["age"])
if __name__ == '__main__':
unittest.main(verbosity=2)
read_yml2_data.yml
- name: 'Tom' age: 13 - name: 'Carl' age: 20 - name: 'Edward' age: 28
運(yùn)行結(jié)果:
========測試開始========
Tom
13
========測試結(jié)束========
========測試開始========
Carl
20
========測試結(jié)束========
========測試開始========
Edward
28
========測試結(jié)束========
4 UnitTest 斷言用法
在 UnitTest中,TestCase 已經(jīng)提供有封裝好的斷言方法進(jìn)行斷言校驗(yàn)。
斷言:用于校驗(yàn)實(shí)際結(jié)果與預(yù)期結(jié)果是否匹型,在斷言的內(nèi)容選擇上,是有要求的。
斷言強(qiáng)調(diào)的是對于整個(gè)測試流程的結(jié)果進(jìn)行判斷,所以斷言的內(nèi)容是極為核心的。
上面的代碼
#coding=utf-8
import unittest
from ddt import ddt
from ddt import file_data
import yaml
# 類之前定義裝飾器,表示在類中要使用ddt了
@ddt
class MyTestCase(unittest.TestCase):
def setUp(self) -> None:
print('{:=^20}'.format("測試開始"))
def tearDown(self) -> None:
print("{:=^20}".format('測試結(jié)束'))
@file_data('read_yml2_data.yml')
def test_yam(self, **kwargs):
# 獲取參數(shù)中key 為 name 的 value
name = kwargs['name']
print(name)
# 這里做斷言,當(dāng)斷言不相等的時(shí)候返回 msg
self.assertEqual(name, 'Tom', msg = 'NotEqual')
# 獲取為 text 的 value
print(kwargs["age"])
if __name__ == '__main__':
unittest.main()
- name: 'Tom' age: 13 - name: 'Carl' age: 20 - name: 'Edward' age: 28

可以看到第一個(gè)例子執(zhí)行正確,后面的例子,執(zhí)行結(jié)果和預(yù)期不一致,返回 NotEqual,左側(cè)的日志可以看到第一個(gè)用例執(zhí)行成功,后面兩個(gè)例子執(zhí)行失敗。
unittest 框架的 TestCase 類提供以下方法用于測試結(jié)果的判斷
| 方法 | 檢查 |
|---|---|
| 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) |
5 UnitTest.skip()用法
假設(shè)我們有很多測試用例,有些我們需要執(zhí)行,有些我們不想執(zhí)行,不想執(zhí)行的測試用例如何才能不執(zhí)行呢,這就需要用到 skip。
Skip用法:
- 在 Case 中,對于不需要運(yùn)行的用例或者特定條件下不執(zhí)行的用例,可以應(yīng)用 skip() 來實(shí)現(xiàn)有條件執(zhí)行,或者絕對性跳過,用于對指定用例進(jìn)行不執(zhí)行操作
- skip通過裝飾器進(jìn)行使用
還是通過案例進(jìn)行講解,下面有 6 個(gè)測試用例,2-5測試用例被屏蔽了,使用的方法不同,
- @unittest.skip(“xxx”)是無條件跳過,xxx為跳過的理由
- unittest.skipIf(1 < 2, ‘xxx'),條件為 True 時(shí)跳過
- @unittest.skipUnless(1 > 2, ‘xxx'),條件為 False 時(shí)跳過,和 skipIf 更好相反
- @unittest.expectedFailure,如果用例執(zhí)行失敗,則不計(jì)入失敗的case數(shù)中
直接看例子更加直觀
skip_t.py
#coding=utf-8
import unittest
class MyTestCase(unittest.TestCase):
def setUp(self) -> None:
pass
def tearDown(self) -> None:
pass
def test_1(self):
print('1')
# 無條件跳過該條用例
@unittest.skip("不想運(yùn)行")
def test_2(self):
print('2')
# 有條件跳過操作,條件為True跳過
@unittest.skipIf(1 < 2, '1 < 2 為True,條件成立,跳過執(zhí)行')
def test_3(self):
print('3')
# 有條件執(zhí)行跳過,條件為False跳過
@unittest.skipUnless(1 > 2, 'Unless的理由,條件為False,跳過執(zhí)行')
def test_4(self):
print('4')
# 如果用例執(zhí)行失敗,則不計(jì)入失敗的case數(shù)中
@unittest.expectedFailure
def test_5(self):
print('5')
self.assertEqual(4, 3, msg = 'NotEqual')
def test_6(self):
print('6')
if __name__ == "__main__":
unittest.main()
執(zhí)行結(jié)果如下,可以看到,test_2,test_3,test_4 跳過,test_5執(zhí)行失敗,但是不計(jì)入case數(shù)中

5 UnitTest測試套件及runner應(yīng)用
測試套件 Suite 作用:
- 用于給測試用例進(jìn)行排序
- 管理測試用例
通過例子講解最容易理解,看一個(gè)最簡單的例子,下面的代碼中有五個(gè)測試用例,程序運(yùn)行的結(jié)果和測試用例在代碼中位置是沒有關(guān)系的,結(jié)果永遠(yuǎn)打印 1 2 3 4 5,這是因?yàn)闇y試用例的執(zhí)行順序默認(rèn)是按照字典順序執(zhí)行的,如何才能控制測試用例的執(zhí)行順序呢,這就需要使用測試套件了。
suite_case.py
import unittest
class MyTestCase(unittest.TestCase):
def setUp(self) -> None:
pass
def tearDown(self) -> None:
pass
def test_2(self):
print("2")
def test_1(self):
print("1")
def test_4(self):
print("4")
def test_3(self):
print("3")
def test_5(self):
print("5")
if __name__ == "__main__":
unittest.main()
運(yùn)行結(jié)果:
1
2
3
4
5
再建一個(gè)py 文件
#coding=utf-8
import unittest
from suite_case import *
# 創(chuàng)建一個(gè)測試套件 list
suite = unittest.TestSuite()
# 方法一,添加測試用例(子元素)到測試套件(集合)
suite.addTest(MyTestCase('test_3'))
suite.addTest(MyTestCase("test_1"))
suite.addTest(MyTestCase("test_5"))
# 套件通過TextTestRunner對象運(yùn)行,功能相當(dāng)于unittest.main()
runner = unittest.TextTestRunner()
runner.run(suite)
我們首先創(chuàng)建一個(gè)測試套件,然后向測試套件中添加測試用例,最后創(chuàng)建 TextTestRunner 對象,調(diào)用 run 函數(shù)運(yùn)行測試用例。這樣我們不僅可以控制測試用例的執(zhí)行順序,還可以控制運(yùn)行哪個(gè)測試用例。
結(jié)果如下:
3
1
5
上面的方法每次添加測試用例都需要調(diào)用 addTest 函數(shù),能不能一次添加多個(gè)測試用例呢,可以的,將測試用例寫成一個(gè)列表,通過addTests 函數(shù)可以一次添加多個(gè)測試用例
#coding=utf-8
import unittest
from suite_case import *
# 創(chuàng)建一個(gè)測試套件 list
suite = unittest.TestSuite()
# 方法二,批量添加測試用例
cases = [MyTestCase('test_3'), MyTestCase('test_1'), MyTestCase('test_5')]
suite.addTests(cases)
# 套件通過TextTestRunner對象運(yùn)行,功能相當(dāng)于unittest.main()
runner = unittest.TextTestRunner()
runner.run(suite)
如果測試用例非常多,或者有多個(gè)文件中的測試用例都需要測試,這樣添加也不是很方便,我們好可以按照文件路徑,將該路徑下需要測試的文件添加進(jìn)測試套件中
#coding=utf-8 import unittest from suite_case import * # 創(chuàng)建一個(gè)測試套件 list suite = unittest.TestSuite() # 方法三,批量運(yùn)行多個(gè)unittest類 test_dir = './' # start_dir 參數(shù)指定文件路徑,pattern 執(zhí)行規(guī)則,'s*.py' 表示以 "s" 開頭,".py" 的都加入測試套件中 discover = unittest.defaultTestLoader.discover(start_dir = test_dir, pattern = 's*.py') runner = unittest.TextTestRunner() runner.run(discover) # 通過 run 函數(shù)運(yùn)行測試用例
還可以執(zhí)行類的名字,執(zhí)行該類下面所有的測試用例,使用 loadTestsFromName 函數(shù)或者 loadTestsFromTestCase 都可以,案例如下:
#coding=utf-8
import unittest
from suite_case import *
# 創(chuàng)建一個(gè)測試套件 list
suite = unittest.TestSuite()
# 方法四,給出文件名和類名,就能測試所有的測試用例
suite.addTests(unittest.TestLoader().loadTestsFromName('suite.MyTestCase'))
# 套件通過TextTestRunner對象運(yùn)行,功能相當(dāng)于unittest.main()
runner = unittest.TextTestRunner()
runner.run(suite)
#coding=utf-8 import unittest from suite_case import * # 創(chuàng)建一個(gè)測試套件 list suite = unittest.TestSuite() # 方法五,給出類名,就能測試所有的測試用例 suite.addTests(unittest.TestLoader().loadTestsFromTestCase(MyTestCase)) # 套件通過TextTestRunner對象運(yùn)行,功能相當(dāng)于unittest.main() runner = unittest.TextTestRunner() runner.run(suite)
6 UnitTest+HTMLTestRunner 自動化實(shí)現(xiàn)
通過 HTMLTestRunner 我們可以將測試結(jié)果生成 html 文件,通過網(wǎng)頁端進(jìn)行查看。步驟如下:
1. 導(dǎo)入環(huán)境
下載 HTMLTestRunner.py 文件,下載地址

點(diǎn)進(jìn)入HTMLTestRunner.py,右鍵另存為就可以下載到本地。
下載后,把HTMLTestRunner.py 文件復(fù)制到 Python 安裝路徑下的 lib 文件夾中(我的安裝路徑是:C:\Users\Administrator\AppData\Local\Programs\Python\Python38\Lib)。在python3中用HTMLTestRunner.py 報(bào) importError“:No module named 'StringIO'解決辦法,原因是官網(wǎng)的是python2語法寫的,看官手動把官網(wǎng)的 HTMLTestRunner.py 改成 python3 的語法。
修改內(nèi)容:
- 第94行,將import StringIO修改成import io
- 第539行,將self.outputBuffer = StringIO.StringIO()修改成self.outputBuffer = io.StringIO()
- 第642行,將if not rmap.has_key(cls):修改成if not cls in rmap:
- 第631行,將print >> sys.stderr, ‘\nTime Elapsed: %s' % (self.stopTime-self.startTime)修改成print(sys.stderr, ‘\nTime Elapsed: %s' % (self.stopTime-self.startTime))
- 第766行,將uo = o.decode(‘latin-1')修改成uo = e
- 第772行,將ue = e.decode(‘latin-1')修改成ue = e
2. 導(dǎo)包
from HTMLTestRunner import HTMLTestRunner
下面就通過案例進(jìn)行演示
suite_case.py 文件
import unittest
class MyTestCase(unittest.TestCase):
def setUp(self) -> None:
pass
def tearDown(self) -> None:
pass
def test_2(self):
print("2")
def test_1(self):
print("1")
def test_4(self):
print("4")
def test_3(self):
print("3")
def test_5(self):
print("5")
if __name__ == "__main__":
unittest.main()
#coding=utf-8 import unittest from suite_case import MyTestCase from HTMLTestRunner import HTMLTestRunner import os suite = unittest.TestSuite() report_path = './report/' report_file = report_path + 'report.html' # 路徑不存在就創(chuàng)建一個(gè)文件夾 if not os.path.exists(report_path): os.mkdir(report_path) else: pass report_name = '測試報(bào)告名稱' report_title = '測試報(bào)告標(biāo)題' report_desc = '測試報(bào)告描述' with open(report_file, 'wb') as report: suite.addTests(unittest.TestLoader().loadTestsFromTestCase(MyTestCase)) # 套件結(jié)合 TextTestRunner 對象進(jìn)行運(yùn)行,相當(dāng)于 unittest.mian() # 如果結(jié)合 HTMLTestRunner 使用,則需要調(diào)用 HTMLTestRunner 中的運(yùn)行器 runner = HTMLTestRunner(stream = report, title = report_title, description = report_desc) runner.run(suite)
運(yùn)行 就會成成 report.html 文件,瀏覽器打開該文件,如下所示:

這樣就生成一個(gè)比較直觀的測試報(bào)告
總結(jié)
到此這篇關(guān)于Python自動化之UnitTest框架實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)Python自動化UnitTest框架實(shí)戰(zhàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Pycharm安裝報(bào)錯(cuò):Cannot detect a launch confi
本文主要介紹了Pycharm安裝報(bào)錯(cuò):Cannot detect a launch configuration解決辦法,具有一定的參考價(jià)值,感興趣的可以了解一下2025-03-03
Django全局啟用登陸驗(yàn)證login_required的方法
這篇文章主要介紹了Django全局啟用登陸驗(yàn)證login_required的方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06
巧妙使用python?opencv庫玩轉(zhuǎn)視頻幀率
這篇文章主要介紹了巧妙使用python?opencv庫玩轉(zhuǎn)視頻幀率的教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
python3+PyQt5重新實(shí)現(xiàn)自定義數(shù)據(jù)拖放處理
這篇文章主要為大家詳細(xì)介紹了python3+PyQt5重新實(shí)現(xiàn)自定義數(shù)據(jù)拖放處理,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04
python 列出面板數(shù)據(jù)所有變量名的示例代碼
在Python中,處理面板數(shù)據(jù)(Panel Data)通常使用pandas庫,特別是當(dāng)數(shù)據(jù)以DataFrame或Panel,這篇文章主要介紹了python 列出面板數(shù)據(jù)所有變量名,需要的朋友可以參考下2024-06-06
Python 實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)-堆棧和隊(duì)列的操作方法
隊(duì)、棧和鏈表一樣,在數(shù)據(jù)結(jié)構(gòu)中非?;A(chǔ)一種數(shù)據(jù)結(jié)構(gòu),同樣他們也有各種各樣、五花八門的變形和實(shí)現(xiàn)方式。這篇文章主要介紹了Python 實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)-堆棧和隊(duì)列的操作方法,需要的朋友可以參考下2019-07-07

