在我們真正的編寫測試用例之前,我們需要了解一下測試框架。目前python自帶的unittest和第三方測試框架pytest這兩個測試框架比較流行,unittest在過去使用的人很多,近兩年pytest有逐漸取代unittest之勢。今天我們先了解一下這兩個測試框架。
unittest
認識unittest
在我們真正的編寫測試用例之前,我們需要了解一下測試框架。
unittest是python自帶的單元測試框架,盡管其主要是為單元測試服務的,但我們也可以用它來做自動化測試。
unittest框架為我們編寫用例提供了如下的能力:
定義用例的能力。unittest框架有一套固有套路,可以讓我們定義測試用例時更加簡單和統一
斷言的能力。unittest框架提供了一系列的斷言
各種執行策略。通過test suit或者擴展的方式,我們可以自定義用例執行的策略
用例編寫
我們先說一下用例的格式吧,我們先寫個測試用例吧:
import unittest
class PrintTest(unittest.TestCase):
def setUp(self):
self.num = 3
print("num0:%s"%self.num)
def test_test1(self):
self.num= self.num+3
print("num1:%s"%(self.num))
self.assertEqual(self.num,6,"self.num發生改變不等于3")
def test_test2(self):
print("num2:%s"%self.num)
def tearDown(self):
self.num = 0
print("num3:%s"%self.num)
if __name__ == '__main__':
unittest.main()
接下來由這個例子給大家講一下unittest如何編寫測試用例
導入unittest包
import unittest
這個就不用我多說了吧,python語言需要用哪個包或者模塊時候一定要先導入才能使用
定義測試類
class PrintTest(unittest.TestCase)
初學者看到這一行就害怕,其實大可不必。暫且把它當作是一個套路吧,測試類的名字你可以隨意取,當然了最好遵循python的PEP8規范,這樣代碼看起來更加整潔美觀。所有的測試類都必須直接或間接的繼承自unittest.TestCase類。總之,這還是套路,記住就好。
用例初始化數據
def setUp(self):
self.num = 3
print("num0:%s"%self.num)
setUp(self)方法是一個測試用例初始化方法,在每個測試用例執行之前都會執行一次,是做數據初始化的好地方。在上面的例子里,我們為每一個測試方法都定義了被測對象--self.num,通俗一點就是每次執行測試用例時,self.num都會被賦值為3
測試用例
def test_test1(self):
self.num= self.num+3
print("num1:%s"%(self.num))
self.assertEqual(self.num,3,"self.num發生改變不等于3")
首先,在unittest測試框中,他會把以test開頭的函數做位測試用例,換句話說,就是每個測試用例的命名已經做了約束,開頭必須是test,只有test開頭的才是測試用例
另外,unittest框架自身也有斷言,斷言是測試用例的一個重要組成部分,斷言的設置直接決定著自動化測試用例的效果。這里強調一下,大家一定要養成在斷言后加入自己業務相關的信息,方便根據自動化報告分析錯誤,可以提高發現問題的效率
測試環境恢復
def tearDown(self):
self.num = 0
print("num3:%s"%self.num)
在測試過程中,是需要對測試后的環境和數據進行恢復的,在unittest測試框架中,tearDown(self)這個方法就是用來恢復測試環境的,每個用例執行完都會執行一遍
這里我在補充一點,如果是測試環境需要初始化和恢復,而且不需要每個測試用例都初始化,我們可以用setUpClass()和tearDownClass()這兩個類,但是必須使用@classmethod裝飾器進行修飾
@classmethod
setUpClass()
self.num=0
這樣表示在當前類里所有測試用例執行之前將self.num賦值為0,在用例執行過程中不再執行,適用于初始化測試環境
@classmethod
tearDownClass()
self.num=0
這樣表示在當前類里所有測試用例執行完之后將self.num恢復為0,在單個用例執行結束后不再執行,適用于恢復測試環境,這里我強調一下這兩個類作用域僅限于當前類里的所有test,不是所有的測試用例py文件
用例執行
if __name__ == '__main__':
unittest.main()
依然是套路,上面的代碼表示,如果直接執行該python文件的話,就運行所有的測試類里的測試用例,也就是運行所有的以test開頭的方法,我們姑且這么認為,unittest.main()是最簡單的用例執行方法
接下來我們將上述代碼運行一遍看看結果
num0:3
num1:6
num3:0
num0:3
num2:3
num3:0
Ran 2 tests in 0.000s
OK
從結果我們可以看到,每個測試用例執行順序是setUp-->test_case-->tearDown
unittest小結
使用unittest的話需要記住下面的幾點
導入unittest
定義繼承自unittest.TestCase的測試類
定義以test開頭的測試方法,這個方法就是測試用例,你可以在一個類里定義n個測試用例
斷言后面的信息盡量和自己業務相關,方便定位問題
unittest.main()是執行測試用例最簡單的方式
pytest
更完善的pytest
近兩年pytest使用的人也越來越多,主要是pytest的擴展性和其他方面的功能要比unittest更完善,最最重要的是,pytest可以兼容unittest,也就是說unittest的測試用例在pytest框架里也能執行,但是要注意的一點是文件名要符合pytest的規則。
pytest用例編寫
我們首先說一下pytest的用例編寫規則:
測試文件以test_開頭(以test結尾也可以)
測試類以Test開頭,并且不能帶有 init 方法
測試函數以test開頭
斷言還是繼承python的斷言assert
現在我們參照這個規則把上面的unittest框架的測試用例用pytest實現
test_print.py
import pytest
class TestPrint:
def setup(self):
self.num = 3
print("num0:%s"%self.num)
def test_test1(self):
self.num= self.num+3
print("num1:%s"%(self.num))
assert self.num==6,"self.num發生改變不等于6"
def test_test2(self):
print("num2:%s"%self.num)
def teardown(self):
self.num = 0
print("num3:%s"%self.num)
if __name__ == '__main__':
pytest.main(['-s', 'test_print.py'])
定義測試類
class TestPrint:
測試類一定要以Test開頭,記住,還是套路,小寫test或者Test放在類名后面都不行,會加載不到里面的測試用例
測試環境數據初始化與恢復
pytest里面依然是使用setup()和teardown()作為初始化數據和恢復測試環境的方法,只不過是全小寫了而已。當然在pytest也存在setup_class()和
teardown_class()這樣的類,用來在當前測試類里所有測試用例執行之前初始化和全部執行完后恢復數據和環境,且不需要再使用@classmethod這樣的裝飾器進行修飾
測試用例執行
pytest在執行測試用例時可以參數化,而且根據不同的需要可以定制化執行結果,我們先簡單看幾個參數:
pytest.main(['-s', 'test_print.py'])
#output:
collected 2 items
test_print.py num0:3
num1:6
.num3:0
num0:3
num2:3
.num3:0
============================== 2 passed in 0.08s ==============================
可以把print的內容打印出來,并輸出執行結果和用時
pytest.main(['-v', 'test_print.py'])
#output:
collecting ... collected 2 items
test_print.py::TestPrint::test_test1 PASSED [ 50%]
test_print.py::TestPrint::test_test2 PASSED [100%]
============================== 2 passed in 0.09s ==============================
這里是不是沒有print的內容了,他會展示每條case的執行結果
當然我們還可以組合使用
pytest.main(['-v','-s', 'test_print.py'])
#output:
collecting ... collected 2 items
test_print.py::TestPrint::test_test1 num0:3
num1:6
PASSEDnum3:0
test_print.py::TestPrint::test_test2 num0:3
num2:3
PASSEDnum3:0
============================== 2 passed in 0.06s ==============================
既可以顯示print內容還可以看到每條用例的執行結果
常用命令參數
命令行 | 解釋 |
---|---|
pytest --version |
顯示版本信息 |
pytest --fixtures |
顯示可用的內置函數 |
pytest -h --help |
顯示參數和配置的幫助信息 |
pytest --lf |
運行上一次運行失敗的用例 |
pytest -x --exitfirst |
第一次失敗后停止 |
pytest --maxfail=2 |
第二(n)次失敗后停止 |
pytest test_mod.py |
運行單個文件中的用例 |
pytest testcase/ |
運行文件夾下的用例 |
pytest -k "MyClass and not method" |
關鍵字表達式(文件名、類名、方法名)運行測試用例 (將運行TestMyClass.test_something 不運行TestMyClass.test_method_simple) |
pytest test_mod.py::test_func |
運行模塊內特指的方法 |
pytest test_mod.py::TestClass::test_method |
運行模塊下類內特指的方法 |
pytest -m slow |
marker運行測試用例(運行所有被裝飾器標記@pytest.mark.slow的用例) |
pytest --pyargs pkg.testing |
從包中運行測試用例(這將要導入import pkg.testing) |
pytest -ra |
運行測試用例(顯示測試總的結果信息,輸出信息的最后) |
pytest -rp |
運行測試用例(顯E示測試通過的結果信息,輸出信息的最后) |
pytest -rE |
運行測試用例(顯示測試錯誤的結果信息,輸出信息的最后) |
pytest -rs |
運行測試用例(顯示測試跳過的結果信息,輸出信息的最后),也可以結合使用 -rfs – 顯示跳過、失敗的 |
pytest -v pytest1.py |
用于顯示每個測試函數的執行結果 |
pytest -q pytest1.py |
只顯示整體測試結果 |
pytest -s pytest1.py |
用于顯示測試函數中print()函數輸出 |
pytest --durations=10 |
獲得最慢的10個測試持續時間表 |
pytest --junitxml=path |
生成一個結果集xml文件,可用于Jenkins 持續集成 |
pytest --pastebin=failed |
為每個失敗的用例創建一個URL |
pytest小結
關于pytest的使用方法我就先介紹這么多了,關于pytest的其他更高階的用法,大家可以去查閱pytest使用手冊
unittes VS pytest
用例編寫
使用unittest編寫測試用例必須遵循以下規則:
1.測試類必須要繼承 unittest.TestCase
2.測試方法必須以test_kai開頭
pyest是Python的第三方測試框架,是基于unittest的擴展框架,比unittest更簡潔高效,使用pytest編寫測試用例必須遵循以下規則:
1.測試文件必須以test開頭或者_test結尾
2.測試方法需以test開頭
3.測試類必須以Test開頭
前置跟后置
1.unittest提供了setUp/tearDown,每個用例運行前、結束后運行一次。setUpClass和tearDownClass,用例執行前、結束后,只運行一次。
2.pyets 可以在函數前使用@pytest.fixture()裝飾器,fixture使用范圍可以是:function(函數級別)、class、module(模塊級別)、package(包級別)、session(多個測試類可以共用一個session)
優勢
1.fixure命名更加靈活,局限性比較小
2.conftest.py 配置里可以實現數據共享,不需要import就能自動找到一些配置,可供多個py文件調用。
3.scope="session" 以實現多個.py跨文件使用一個session來完成多個用例
斷言
1.unittest提供了assertEqual、assertIn、assertTrue、assertFalse等
2.pytest直接在assert 后面接表達式
失敗重跑
1.unittest無此功能
2.pytest支持
參數化
1.unittest需要依賴于ddt庫
2.pytest直接使用@pytest.mark.parametrize裝飾器
擴展性
與unittest相比,pytest具有很多第三方插件,并且可以自定義擴展,比較好用的如pytest-selenium(集成selenium)、pytest-html(完美html測試報告生成)、pytest-xdist(多CPU分發)等