一:
pytest 是python的一套全功能的測試框架. 優點如下:
1、操作簡單,支持多組數據參數化, 支持用例的skip和xfail;
2、支持簡單的單元測試和復雜的功能測試,還可以做UI和接口自動化測試;
3、pytest有很多第三方的插件並且支持定義擴展; 如失敗重新執行, 斷言失敗也繼續運行,自定義出錯停止, 自定義mark標記靈活運行用例....
4、可以很好的集成CI
二:
框架結構:
類似的setup,teardown同樣更靈活,
模塊級(setup_module/teardown_module)模塊始末,全局的
函數級(setup_function/teardown_function)不在類中的函數有用
類級(setup_class/teardown_class)只在類中前后運行一次。
方法級(setup_method/teardown_methond)運行在類中方法始末
session() 跨文件級
三:
Pytest調用方式和用例設計原則:
1. Pytest/py.test(終端,命令行,pycharm都行,可配置pycharm
使用pytest方式執行)
Pytest –v (最高級別信息—verbose -q靜默模式)
pytest -v -s -q 文件名 (s是帶控制台輸出結果,也是輸出詳細) 2. pytest將在當前目錄及其子目錄中運行test _ * .py或* _test.py形式
3. 以test_開頭的函數,以Test開頭的類,以test_開頭的方法。所有包package都要有__init__.py文件。
4. Pytest可以執行unittest框架寫的用例和方法
四:
Fixture: 對於setup和teardown 有些場景不適用, 同一個類里的用例 有的用例需要執行setup 有些用例不需要;這時候用fixture比較好用.
用法:
1、對於setup,teardown,可以不起這兩個名字,所以命名方式靈活。(可獨立命名,聲明后激活)
2、數據共享。在conftest.py配置里寫方法可以實現數據共享,不需要import導入。可以跨文件共享, 使用的文件與conftest文件在同一文件夾下;
3、 scope的層次及神奇的yield組合相當於各種setup 和 teardown的所有文件.
不想原測試方法有任何改動,或全部都自動實現自動應用,沒特例,也都不需要返回值時可以選擇自動應用
解決: 使用fixture中參數autouse=True實現
步驟:在方法上面加@pytest.fixture(autouse=True) 使用@pytest.mark.usefixtures
步驟:在測試方法上加@pytest.mark.usefixtures("start")
測試離不開數據,為了數據靈活,一般數據都是通過參數傳的
解決:fixture通過固定參數request傳遞;
步驟:在fixture中增加@pytest.fixture(params=[1, 2, 3, 'linda']) ; 在方法參數寫request
import pytest
#module模塊級,只在運行模塊式執行一次。如果不加范圍,默認會在該模塊中的每一個方法執行前執行;autouse=True設置后實現自動應用,后面模塊內所有的函數后面不用加方法名@pytest.fixture(scope="module", autouse=True)def open_browser():
print("\n打開瀏覽器,打開網站首頁")
yield #模塊執行完case后 在最后執行一遍teardown操作。
print('執行teardown')
print('最后關閉瀏覽器')
def test_soso(login): #login函數寫在 conftest文件中 @pytest.fixture() def login():
print('case1: 登際后執行搜索')
def test_cakan():
print('case2:不登陸直接操作')
def test_cart(login):
print('case3,登陸后進行購物')
或者使用裝飾器方法:不改變原有代碼的情況下增加新功能,靈活的讓某個方法經過裝飾后具備 setup teardown 或其它功能項
@pytest.fixture()
def open_browser():
print("\n打開瀏覽器,打開網站首頁")
yield
print('執行teardown')
print('最后關閉瀏覽器')
@pytest.mark.usefixtures('open_browser')
def test_soso(): #def test_soso(open_browser) 效果是一樣的 但是這樣會改變原代碼結構,其他地方有復用的話就可能會有問題
print('case1: 登際后執行搜索')
Pip install pytest-sugar
pip install pytest-rerunfailures Pip install pytest-xdist
Pip install pytest-assume
Pip intall pytest-html
test_user_data = ["zhangsan", "lisi"]
@pytest.fixture()
def login_r(request):
user = request.param
print("\n打開首頁准備登陸,登陸用戶:%s" % user)
return user
@pytest.mark.parametrize('login_r',test_user_data,indirect=True)
def test_login_s(login_r):
print(login_r)
等同於:
@pytest.fixture(params=test_user_data)
def login_r(request):
user = request.param
print("\n打開首頁准備登陸,登陸用戶:%s" % user)
return user
def test_login_s(login_r):
print(login_r)
多組數據:
test_user_data1 = [{"user": "linda", "password": "888888"},
{"user": "servenruby", "password": "123456"},
{"user": "test01", "password": ""}]
test_user_data2 = [{"q": "中國平安", "count": 3, "page": 1},
{"q": "阿里巴巴", "count": 2, "page": 2},
{"q": "pdd", "count": 3, "page": 1}]
@pytest.fixture(scope="module")
def login_r(request):
# 這是接收傳入的參數,接收一個參數
user = request.param['user']
password = request.param['password']
print("\n用戶名:%s,密碼:%s" % (user, password))
@pytest.fixture(scope="module")
def query_param(request):
# 這是接收傳入的參數,接收一第二個參數
q = request.param['q']
count = request.param['count']
page = request.param['page']
print("查詢的搜索詞:%s" % q)
return request.param
# 這是pytest的參數化數據驅動,indeirect=True 是把login_r當作函數去執行,不寫的話會當它是參數
# 從下往上執行
# 兩個數據進行組合測試有3*3個測試用例執行(test_user_data1的個數*test_user_data2的個數)
@pytest.mark.parametrize("query_param", test_user_data2, indirect=True)
@pytest.mark.parametrize("login_r", test_user_data1, indirect=True)
def test_login(login_r, query_param):
# 登陸用例
print(login_r)
print(query_param)
七
mark中的skip與xfail
-
調試時不想運行這個用例
-
標記無法在某些平台上運行的測試功能,
-
當前的外部資源不可用時跳過(如果測試數據是從數據庫中取到的,連接數據庫的功能如果返回結 果未成功就不執行跳過,因為執行也都報錯)
-
在某些版本中執行,其他版本中跳過。
解決:@pytest.mark.skip跳過這個測試用例,可以加條件skipif,在滿足某些條件下才希望通過,否則跳過這個測試。
-
功能測試尚未實施或尚未修復的錯誤,當測試通過時盡管預計會失敗(標記為pytest.mark.xfail), 它是一個xpass,將在測試摘要中報告。
-
你希望測試由於某種情況而就應該失敗
解決:@pytest.mark.xfail
八
場景:只執行符合要求的某一部分用例 可以把一個web項目划分多個模塊,然后指定模塊名稱執行。
App自動化時,如果想Android和IOS公用一套代碼時,也可以使用標記功能,標明哪些是IOS 的用例,哪些是Android的,運行代碼時指定mark名稱運行就可以。
在測試用例方法上加@pytest.mark.webtest 執行:
-s參數: 輸出所有測試用的print信息 -m:執行自定義標記的相關用例
pytest -s test_mark_zi_09.py
pytest -s test_mark_zi_09.py -m=webtest #只執行標記為webtest的用例
pytest -s test_mark_zi_09.py -m apptest
pytest -s test_mark_zi_09.py -m "not ios" #不執行標記為 ios的用例
九
只執行符合要求的某一部分用例,通過類與方法的命名實現。通常編寫測試方法時
方法一:直接輸入文件名,類名
pytest test_class_01.py
pytest -v -s test_class_01.py
pytest -v test_class_01.py::TestClass
pytest -v test_class_01.py::TestClass::test_one #只執行該測試類中的test_one
方法二 :使用-k
pytest -k "TestClass and test_one" #只執行該測試類中的test_one
pytest -k "TestClass or test_one"
TestClass是類名,and是運算符,還可以是and not...,test_one是方法名中含有的信息。
十
正常全部執行完成后才能停止,如果想遇到錯誤時停止測試:- x;也可以當用例錯誤個數n達到指定數量時,停止測試:- - maxfail=n
pytest -x -v -s test_class_01.py
pytest -x -v -s test_class_01.py - -maxfail=2 #錯誤數達到2個時停止測試
十一
測試失敗后要重新運行n次,要在重新運行之間添加延遲時間, 間隔n秒再運行。
執行:
安裝:pip install pytest-rerunfailures
pytest --reruns 3 -v -s test_reruns_10.py pytest -v - -reruns 5 --reruns-delay 1
十二
一個方法中寫多條斷言,通常第一條過去,下面就不執行了。我們想報錯也都執行一下。
執行:安裝pip3 install pytest-assume
pytest.assume(1==4) #注意此處用的不是assert 斷言
pytest.assume(2==4)
十三
測試用例1000條,一個用例執行1鍾,一個測試人員執行需要1000分 鍾。通常我們會用人力成本換取時間成本,加幾個人一起執行,時間就會 縮短。如果10人一起執行只需要100分鍾,這就是一種並行測試,分布式場景。
解決:pytest分布式執行插件:pytest-xdist,多個CPU或主機執行 前提:用例之間都是獨立的,沒有先后順序,隨機都能執行,可重復運行
不影響其他用例。
安裝:Pip3 install pytest-xdist
多個CPU並行執行用例,直接加-n 3是並行數量:pytest -n 3 在多個終端下一起執行
Pytest -v -s --html=report.html #生成html報告 利用 pytest-html