轉載自:https://blog.csdn.net/qq_42610167/article/details/101204066
感謝博主:天蠍座的測試之旅
文章目錄
- 一:pytest的介紹,安裝,框架結構,執行方式
- 二:Pytest -斷言、跳過及運行
- 1,Pytest -斷言、跳過及運行
- 2,mark中的skip(跳過)
- 3,mark中的xfail(失敗)
- 4,使用自定義標記mark只執行部分用例
- 5,文件名類名方法執行部分用例
- 6,-k 組合調用執行部分用例
- 三, Pytest -fixture
- 1. 如下: 定義一個函數名叫open_url的fixture前后置,前置為打開鏈接,后置為退出瀏覽器
- 2.在我們要用這個前后置的類前面 我們用@pytest.mark.usefixtures(fixture函數名)
- 3.進階方法:conftest中定義多個fixture,一個fixture可以是另一個fixture的前后置,期間還是用field隔開前后置
- 4.說到上面的多個fixture調用,很多人就會疑惑,會不會fixture之間相互沖突。
- 5,fixture的自動應用autouse
- 四,參數化與數據驅動框架實現
- 五,第三方插件
- 1,調整測試用例的執行順序
- 2, 執行用例遇到錯誤停止
- 3,執行用例失敗后重新運行
- 4,多條斷言前面報錯后面依然執行
- 5,多線程並行與分布式執行
- 6,其他有意思的插件
- 7,使用pytest執行unittest的測試用例
- 8,pytest-html生成報告
- 六,日志管理及代碼覆蓋率
- 1, pytest中logging的應用
- 2, 日志及級別的含義
- 3, 代碼覆蓋率-多用在單元測試中
- 4,allure測試報告框架
- 5,定制報告
- 6,pytest運行指定用例
- 7,按重要性級別進行一定范圍測試
- 8, 為測試添加詳說明@allure.description;@allure.title;
- 9, 鏈接@allure.link @allure.issue @allure.testcase
- 七, 單元自動化測試pytest和allure在測試中應用 自動執行
一:pytest的介紹,安裝,框架結構,執行方式
1,特點
:1. 簡單靈活,容易上手;支持參數化; 測試用例的skip和xfail 處理;
2. 能夠支持簡單的單元測試和復雜的功能測試,還可以用來做 selenium/appium等自動化測試、接口自動化測試 (pytest+requests);
3. pytest具有很多第三方插件,並且可以自定義擴展, 比較好 用的如 pytest-allure(完美html測試報告生成) pytest-xdist (多CPU分發)等;
4. 可以很好的和jenkins集成;**
2,安裝
pytest安裝,導入相關依賴庫
Pip install –U pytest U表示升級
Pip install sugar
pip install pytest-rerunfailures
Pip install pytest-xdist
Pip install pytest-assume
Pip intall pytest-html …
Pip list查看
Pytest –h 幫助**
3, pytest的框架結構
Import pytest 類似的setup,teardown同樣更靈活
模塊級 (setup_module/teardown_module)
全局的,整個.py模塊開始前和結束后調用一次
函數級 (setup_function/teardown_function)
只對函數用例生效(不在類內),每個函數級用例開始和結束時調用一次
類級 (setup_class/teardown_class)
只在類前后運行一次(在類中)。
方法級 (setup_method/teardown_methond)
運行在類中方法始末
類里面的(setup/teardown):運行在調用方法的前后
類中運行順序:setup_class>setup_method>setup>用例>teardown>teardown_method>teardown_class
4,執行方式
Pytest/py.test(終端,命令行,pycharm可配置pytest方式執行)
- Pytest –v (最高級別信息—verbose)
- pytest -v -s filename 3.Pytest-q (靜默)
(輸出打印)
多種執行方式
1.pytest將在當前目錄及其子目錄中運行test _ * .py或* test.py形 式的所有文件。
2.以test_開頭的函數,以Test開頭的類,以test_開頭的方法。所有包 package都要有__init_.py文件。
3.Pytest可以執行unittest框架寫的用例和方法 - 可以在pytest.ini文件中自定義要運行的測試路徑、文件名、類名和方法名等。
二:Pytest -斷言、跳過及運行
1,Pytest -斷言、跳過及運行
2,mark中的skip(跳過)
3,mark中的xfail(失敗)
])
4,使用自定義標記mark只執行部分用例
1.mark標記
以下用例,標記test_send_http()為webtest
# content of test_server.py
import pytest
@pytest.mark.webtest
def test_send_http():
pass # perform some webtest test for your app
def test_something_quick():
pass
def test_another():
pass
class TestClass:
def test_method(self):
pass
if __name__ == "__main__":
pytest.main(["-s", "test_server.py", "-m=webtest"])
只運行用webtest標記的測試,cmd運行的時候,加個-m 參數,指定參數值webtest
```py
pytest -v -m webtest
如果不想執行標記webtest的用例,那就用”not webtest”
pytest -v -m “not webtest”
import pytest
@pytest.mark.webtest
def test_send_http():
pass # perform some webtest test for your app
def test_something_quick():
pass
def test_another():
pass
class TestClass:
def test_method(self):
pass
if __name__ == "__main__":
pytest.main(["-s", "test_server.py", "-m='not webtest'"])
5,文件名::類名::方法執行部分用例
2.-v 指定的函數節點id
如果想指定運行某個.py模塊下,類里面的一個用例,如:TestClass里面testmethod用例
每個test開頭(或_test結尾)的用例,函數(或方法)的名稱就是用例的節點id,指定節點id運行用-v 參數
pytest -v test_server.py::TestClass::test_method
當然也能選擇運行整個class
pytest -v test_server.py::TestClass
也能選擇多個節點運行,多個節點中間空格隔開
pytest -v test_server.py::TestClass test_server.py::test_send_http
6,-k 組合調用執行部分用例
.-k 匹配用例名稱
可以使用-k命令行選項指定在匹配用例名稱的表達式
pytest -v -k http
您也可以運行所有的測試,根據用例名稱排除掉某些用例:
pytest -k “not send_http” -v
也可以同時選擇匹配 “http” 和“quick”
pytest -k “http or quick” -v
三, Pytest -fixture
pytest 相較於 unittest 最為跳躍的一點應該就是 fixture 機制
對於unittest來說,每個用例的類中都需要去寫入setUp和tearDown。也就是我們所說的前置和后置,
而不可避免的,很多用例的前置和后置都是一樣(例如很多用例都需要前置登錄,后置退出),於是我們需要重復的復制粘貼,這樣導致工作量增加,代碼量也增加,界面也顯得冗雜。
所以此時pytest中fixture機制便要閃亮登場了。
通俗的講: fixture = 前置+后置
而方便的是:如果很多用例都有同樣的前置和后置,那么我就只實現一個,然后需要的用例就去調用就好了。
1.機制:與測試用例同級,或者是測試用例的父級,創建一個conftest.py文件。
2.conftest.py文件里:放所有的前置和后置。 不需要用例.py文件主動引入conftest文件。
3.定義一個函數:包含前置操作+后置操作。
4.把函數聲明為fixture :在函數前面加上 @pytest.fixture(作用級別=默認為function)
5.fixture的定義。
如果有返回值,那么寫在yield后面。(yield的作用就相當於return)
在測試用例當中,調用有返回值的fixture函數時,函數名稱就是代表返回值。
在測試用例當中,函數名稱作為用例的參數即可。
1. 如下: 定義一個函數名叫open_url的fixture前后置,前置為打開鏈接,后置為退出瀏覽器
@pytest.fixture(scope=“class”) #定義scope的范圍
def open_url():
# 前置
driver = webdriver.Chrome()
driver.get(url) #url為鏈接地址
yield driver #yield之前代碼是前置,之后的代碼就是后置。
# 后置
driver.quit()
這樣我們就定義了一個叫做 open_url 的 fixture
2.在我們要用這個前后置的類前面 我們用@pytest.mark.usefixtures(fixture函數名)
就可以直接調用上面定義好的這個前后置
可以看到 在TestLogin 這個類中 我們不再去編寫setup 和 teardown. 直接寫我們的中間過程就可以了。是不是很方便了?
3.進階方法:conftest中定義多個fixture,一個fixture可以是另一個fixture的前后置,期間還是用yield隔開前后置
如上圖中可以看到我class中另外還引用了一個名為refresh_page的fixture,直接上代碼:
# 刷新頁面 - 定義的第二個fixture
@pytest.fixture
def refresh_page(open_url):
yield
open_url.refresh()
直接將open_url作為了另一個fixture的前置引用進來,用yield隔開,當用例中執行完open_url前后置后,再執行了一次refresh的后置。
執行順序: open_url yield 之前代碼 – 用例代碼 – open_url yield 之后代碼 --》 refresh_page yield 之后代碼
是不是很妙,可以解決許多用例流程環環相扣時的麻煩。
4.說到上面的多個fixture調用,很多人就會疑惑,會不會fixture之間相互沖突。
當然是不會了,fixture在conftest.py當中就已經決定了他的用例域,他會主動去區分你這個fixture是作用在哪個用例域。
首先我們看一下框架中對於fixture函數的定義:
scope:定義用例域的范圍:
function:默認范圍,每一個函數或方法都會調用,不填寫時便是它
class:每一個類調用一次
module: 每一個.py文件調用一次,文件中可以有多個function和class
session:多個文件調用一次,可以跨文件,如在.py文件中,每一個.py文件就是module
范圍:
session > module > class > function
所以在調用時各個fixture之間並不會相互沖突。
5,fixture的自動應用autouse
autouse調用例子:**
當管理用例比較多的時候,這種方法比較方便高效,但是用該功能時也要小心,一定要注意fixture的作用范圍。需要注意的是,當使用這種方式時,就不能使用返回值的功了。autouse默認設置為False。當默認為False,就可以選擇用上面兩種方式來試用fixture。當設置為True時,所有的test都會自動調用這個fixture。autouse遵循scope="關鍵字參數"規則:當scope="session"時,無論怎樣定義只運行一次;當scope="module"時,每個py文件只運行一次;當scope="class"時,每個class只運行一次(但是一個文件中包括function和class時,會在每個function(不在class中)運行一次);當scope="function"時,每個function運行一次;
‘’’
平常寫自動化用例會寫一些前置的fixture操作,用例需要用到就直接傳該函數的參數名稱就行了。當用例很多的時候,每次都傳這個參數,會比較麻煩。
fixture里面有個參數autouse,默認是Fasle沒開啟的,可以設置為True開啟自動使用fixture功能,這樣用例就不用每次都去傳參了
設置autouse=True
autouse設置為True,自動調用fixture功能
start設置scope為module級別,在當前.py用例模塊只執行一次,autouse=True自動使用[圖片]open_home設置scope為function級別,
每個用例前都調用一次,自動使用
import pytest
@pytest.fixture(scope="module",autouse=True)
def start(request):
print("\n----開始執行module------")
print('module : %s'% request.module.__name__)
print('------啟動瀏覽器-------')
yield
print("------結束測試 end!----------")
@pytest.fixture(scope="function",autouse=True)
def open_home(request):
print("function:%s \n--回到首頁--"% request.function.__name__)
def test_01():
print('----用例01-----')
def test_02():
print('----用例02-----')
if __name__ == '__main__':
pytest.main(["-s","autouse.py"])
執行結果
----開始執行module------
module : autouse
------啟動瀏覽器-------
function:test_01
--回到首頁--
.----用例01-----
function:test_02
--回到首頁--
.----用例02-----
------結束測試 end!----------
四,參數化與數據驅動框架實現
參數化1
import pytest @pytest.fixture(params=[1, 2, 3]) def need_data(request): # 傳入參數request 系統封裝參數 return request.param # 取列表中單個值,默認的取值方式 class Test_ABC: def test_a(self, need_data): print("------->test_a") assert need_data != 3 # 斷言need_data不等於3
參數化2
1、直接傳入測試數據
import pytest class Test_ABC: def setup_class(self): print("------->setup_class") def teardown_class(self): print("------->teardown_class") @pytest.mark.parametrize("a,b",[(1,2),(0,3)]) #直接傳入數據 def test_a(self, a, b): print("test data:a=%d,b=%d" % (a, b)) assert a + b == 3
2、使用函數返回值傳入數據
import pytest def return_test_data(): return [(1, 2), (0, 3)] class Test_ABC: def setup_class(self): print("------->setup_class") def teardown_class(self): print("------->teardown_class") @pytest.mark.parametrize("a,b", return_test_data()) # 使用函數返回值的形式傳入參數值 def test_a(self, a, b): print("test data:a=%d,b=%d" % (a, b)) assert a + b == 3
結果與1中的結果一致。
參數化3
案例一
import pytest test_user_data=['linda','sai','tom'] @pytest.fixture(scope='module') def login(request): user=request.param print('打開首頁登陸%s'%user) return user #indirect=True是把login當作函數去執行 @pytest.mark.parametrize('login',test_user_data,indirect=True) def test_cart(login): usera=login print('不同用戶添加購物車%s'%usera) assert usera!='' Process finished with exit code 0 打開首頁登陸linda PASSED [ 33%]不同用戶添加購物車linda 打開首頁登陸sai PASSED [ 66%]不同用戶添加購物車sai 打開首頁登陸tom PASSED [100%]不同用戶添加購物車tom
案例二
import pytest test_user_data=[ {'user':'linda','password':'8888'}, {'user':'servenruby','password':'123456'}, {'user':'test01','password':''} ] @pytest.fixture(scope='module') def login_r(request): #可以通過dict形式,雖然傳遞一個參數,但通過key的方式可以達到累死傳入多個參數的效果 user=request.param['user'] pwd=request.param['password'] print('\n打開首頁准備登陸,登陸用戶%s,密碼%s'%(user,pwd)) if pwd: return True else: return False #這是pytest參數化驅動,indeirect=True是把login_r當作函數去執行 @pytest.mark.parametrize('login_r',test_user_data,indirect=True) def test_cart(login_r): #登陸用例 a=login_r print('測試用例中login_r的返回值%s'%a) assert a,'失敗原因,密碼為空' 開首頁准備登陸,登陸用戶linda,密碼8888 PASSED [ 33%]測試用例中login_r的返回值True 打開首頁准備登陸,登陸用戶servenruby,密碼123456 PASSED [ 66%]測試用例中login_r的返回值True 打開首頁准備登陸,登陸用戶test01,密碼 FAILED [100%]測試用例中login_r的返回值False 打開首頁准備登陸,登陸用戶linda,密碼8888 PASSED [ 33%]測試用例中login_r的返回值True 打開首頁准備登陸,登陸用戶servenruby,密碼123456 PASSED [ 66%]測試用例中login_r的返回值True 打開首頁准備登陸,登陸用戶test01,密碼 FAILED [100%]測試用例中login_r的返回值False test_mark_param_request2.py:19 (test_cart[login_r2]) login_r = False @pytest.mark.parametrize('login_r',test_user_data,indirect=True) def test_cart(login_r): #登陸用例 a=login_r print('測試用例中login_r的返回值%s'%a) > assert a,'失敗原因,密碼為空' E AssertionError: 失敗原因,密碼為空 E assert False
參數化3*3

import pytest
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']
pwd=request.param['password']
print('\n用戶名:%s,密碼:%s'%(user,pwd))
@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)
pytest_mark_request3.py::test_login[login_r1-query_param0] ✓ 44% ████▌ 查詢的搜索詞pdd
None
{'q': 'pdd', 'count': 3, 'page': 1}
pytest_mark_request3.py::test_login[login_r1-query_param2] ✓ 56% █████▋
用戶名:linda,密碼:888888
None
{'q': 'pdd', 'count': 3, 'page': 1}
pytest_mark_request3.py::test_login[login_r0-query_param2] ✓ 67% ██████▋
用戶名:test01,密碼:
None
{'q': 'pdd', 'count': 3, 'page': 1}
pytest_mark_request3.py::test_login[login_r2-query_param2] ✓ 78% ███████▊ 查詢的搜索詞阿里巴巴
None
{'q': '阿里巴巴', 'count': 2, 'page': 2}
pytest_mark_request3.py::test_login[login_r2-query_param1] ✓ 89% ████████▉ 查詢的搜索詞中國平安
None
{'q': '中國平安', 'count': 3, 'page': 1}
pytest_mark_request3.py::test_login[login_r2-query_param0] ✓ 100% ██████████
五,第三方插件
1,調整測試用例的執行順序
場景:未考慮按自然順序執行時,或想變更執行順序,比如增加 數據的用例要先執行,再執行刪除的用例。測試用例默認是按名 稱順序執行的。
• 解決:
• 安裝:pip install pytest-ordering
• 在測試方法上加下面裝飾器
•@pytest.mark.last —最后一個執行
• @pytest.mark.run(order=1)—第幾個執行
pytest默認按字母順序去執行的
import pytest
@pytest.mark.run(order=1)
def test_01():
print('test01')
@pytest.mark.run(order=2)
def test_02():
print('test01')
@pytest.mark.last
def test_06():
print('test01')
def test_04():
print('test01')
def test_05():
print('test01')
@pytest.mark.run(order=3)
def test_03():
print('test01')
pytest_order.py::test_01 PASSED [ 16%]test01
pytest_order.py::test_02 PASSED [ 33%]test01
pytest_order.py::test_03 PASSED [ 50%]test01
pytest_order.py::test_04 PASSED [ 66%]test01
pytest_order.py::test_05 PASSED [ 83%]test01
pytest_order.py::test_06 PASSED [100%]test01
2, 執行用例遇到錯誤停止
• 正常全部執行完成后才能停止,如果想遇到錯誤時停止測試: -x;也可以當用例錯誤個數n達到指定數量時,停止測試:- - maxfail=n
• 執行:
• pytest -x -v -s 文件名.py ------- -x是遇到錯誤就停止
• pytest -x -v -s 文件名.py —maxfail=2 ------- --maxfail=2 是遇到兩個錯誤就停止
3,執行用例失敗后重新運行
**場景:
• 測試失敗后要重新運行n次,要在重新運行之間添加延遲時 間,間隔n秒再運行。
• 執行:
• 安裝:pip install pytest-rerunfailures
• pytest -v - -reruns 5 --reruns-delay 1 —每次等1秒 重試5次
4,多條斷言前面報錯后面依然執行
pip3 install pytest-assume 斷言后繼續執行,但要修改斷言**
@pytest.mark.parametrize(('x', 'y'), [(1, 1), (1, 0), (0, 1)])
def test_assume(x, y):
pytest.assume(x == y)
pytest.assume(3 == 4)
pytest.assume(5 == 9)
5,多線程並行與分布式執行
場景:測試用例1000條,一個用例執行1鍾,一個測試人員執行需要1000分 鍾。通常我們會用人力成本換取時間成本,加幾個人一起執行,時間就會縮
短。如果10人一起執行只需要100分鍾,這就是一種並行測試,分布式場景。
解決:pytest分布式執行插件:pytest-xdist,多個CPU或主機執行
前提:用例之間都是獨立的,沒有先后順序,隨機都能執行,可重復運行不 影響其他用例。
安裝:Pip3 install pytest-xdist
• 多個CPU並行執行用例,直接加-n 3是並行數量:pytest -n 3 • 在多個終端下一起執行
import pytest
import time
@pytest.mark.parametrize('x',list(range(10)))
def test_somethins(x):
time.sleep(1)
pytest -v -s -n 5 test_xsdist.py ----一次執行5個
運行以下代碼,項目結構如下
web_conf_py是項目工程名稱
│ conftest.py
│ __init__.py
│
├─baidu
│ │ conftest.py
│ │ test_1_baidu.py
│ │ test_2.py
│ │ __init__.py
│
├─blog
│ │ conftest.py
│ │ test_2_blog.py
│ │ __init__.py
代碼參考:
# web_conf_py/conftest.py
import pytest
@pytest.fixture(scope="session")
def start():
print("\n打開首頁")
return "yoyo"
# web_conf_py/baidu/conftest.py
import pytest
@pytest.fixture(scope="session")
def open_baidu():
print("打開百度頁面_session")
# web_conf_py/baidu/test_1_baidu.py
import pytest
import time
def test_01(start, open_baidu):
print("測試用例test_01")
time.sleep(1)
assert start == "yoyo"
def test_02(start, open_baidu):
print("測試用例test_02")
time.sleep(1)
assert start == "yoyo"
if __name__ == "__main__":
pytest.main(["-s", "test_1_baidu.py"])
# web_conf_py/baidu/test_2.py
import pytest
import time
def test_06(start, open_baidu):
print("測試用例test_01")
time.sleep(1)
assert start == "yoyo"
def test_07(start, open_baidu):
print("測試用例test_02")
time.sleep(1)
assert start == "yoyo"
if __name__ == "__main__":
pytest.main(["-s", "test_2.py"])
# web_conf_py/blog/conftest.py
import pytest
@pytest.fixture(scope="function")
def open_blog():
print("打開blog頁面_function")
# web_conf_py/blog/test_2_blog.py
import pytest
import time
def test_03(start, open_blog):
print("測試用例test_03")
time.sleep(1)
assert start == "yoyo"
def test_04(start, open_blog):
print("測試用例test_04")
time.sleep(1)
assert start == "yoyo"
def test_05(start, open_blog):
'''跨模塊調用baidu模塊下的conftest'''
print("測試用例test_05,跨模塊調用baidu")
time.sleep(1)
assert start == "yoyo"
if __name__ == "__main__":
pytest.main(["-s", "test_2_blog.py"])
正常運行需要消耗時間:7.12 seconds
E:\YOYO\web_conf_py>pytest
============================= test session starts =============================
platform win32 -- Python 3.6.0, pytest-3.6.3, py-1.5.4, pluggy-0.6.0
rootdir: E:\YOYO\web_conf_py, inifile:
plugins: xdist-1.23.2, metadata-1.7.0, html-1.19.0, forked-0.2
collected 7 items
baidu\test_1_baidu.py .. [ 28%]
baidu\test_2.py .. [ 57%]
blog\test_2_blog.py ... [100%]
========================== 7 passed in 7.12 seconds ===========================
設置並行運行數量為3,消耗時間:3.64 seconds,大大的縮短了用例時間
E:\YOYO\web_conf_py>pytest -n 3
============================= test session starts =============================
platform win32 -- Python 3.6.0, pytest-3.6.3, py-1.5.4, pluggy-0.6.0
rootdir: E:\YOYO\web_conf_py, inifile:
plugins: xdist-1.23.2, metadata-1.7.0, html-1.19.0, forked-0.2
gw0 [7] / gw1 [7] / gw2 [7]
scheduling tests via LoadScheduling
....... [100%]
========================== 7 passed in 3.64 seconds ===========================
6,其他有意思的插件
這里就不多說了,喜歡的可以自己研究下
7,使用pytest執行unittest的測試用例
執行unitest就和原來一樣,盡量不要混合使用搞那些花里胡哨的,用哪個就哪個,就不多說了
8,pytest-html生成報告
pytest-HTML是一個插件,pytest用於生成測試結果的HTML報告。兼容Python 2.7,3.6
pytest-html
1.github上源碼地址【https://github.com/pytest-dev/pytest-html】
2.pip安裝
$ pip install pytest-html
3.執行方法
$ pytest --html=report.html
html報告
1.打開cmd,cd到需要執行pytest用例的目錄,執行指令:pytest --html=report.html
2.執行完之后,在當前目錄會生成一個report.html的報告文件,顯示效果如下
指定報告路徑
1.直接執行"pytest --html=report.html"生成的報告會在當前腳本的同一路徑,如果想指定報告的存放位置,放到當前腳本的同一目錄下的report文件夾里
pytest --html=./report/report.html
2.如果想指定執行某個.py文件用例或者某個文件夾里面的所有用例,需加個參數。具體規則參考【pytest文檔2-用例運行規則】
報告獨立顯示
1.上面方法生成的報告,css是獨立的,分享報告的時候樣式會丟失,為了更好的分享發郵件展示報告,可以把css樣式合並到html里
$ pytest --html=report.html --self-contained-html
顯示選項
默認情況下,“ 結果”表中的所有行都將被展開,但具測試通過的行除外Passed。
可以使用查詢參數自定義此行為:?collapsed=Passed,XFailed,Skipped。
更多功能
1.更多功能查看官方文檔【https://github.com/pytest-dev/pytest-html】
六,日志管理及代碼覆蓋率
1, pytest中logging的應用
2, 日志及級別的含義
自動化測試用例的調試信息非常有用,可以讓我們知道現在的運行情況到,執行到哪步以及相應的出錯信息等,可以在pytest里面,有時並不會輸出所有信息,比如默認情況下pass的測試用例是沒有print輸出的。本文將介紹如何在pytest里面實時顯示所有的log信息。
1. 用print輸出log信息
slowTest_print.py
import time
def test_1():
print 'test_1'
time.sleep(1)
print 'after 1 sec'
time.sleep(1)
print 'after 2 sec'
time.sleep(1)
print 'after 3 sec'
assert 1, 'should pass'
def test_2():
print 'in test_2'
time.sleep(1)
print 'after 1 sec'
time.sleep(1)
print 'after 2 sec'
time.sleep(1)
print 'after 3 sec'
assert 0, 'failing for demo purposes'
運行上述程序,pytest會capture所有的輸出,保存直到所有的測試用例都執行結束,並且只輸出那些失敗的測試用例的信息,對於成功的測試用例,沒有print的信息顯示。
從下面的運行結果,如果需要查看test_1()的運行情況,沒有log信息可看,print沒有顯示。
C:\Users\yatyang\PycharmProjects\pytest_example>pytest -v slowTest_print.py
============================= test session starts =============================
platform win32 -- Python 2.7.13, pytest-3.0.6, py-1.4.32, pluggy-0.4.0 -- C:\Python27\python.exe
cachedir: .cache
metadata: {'Python': '2.7.13', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': {'py': '1.4.32', 'pytest': '3.0.6', 'pluggy': '0.4.0'}, 'JAVA_HOME': 'C:\\Program Files (x86)\\Java\\jd
k1.7.0_01', 'Plugins': {'html': '1.14.2', 'metadata': '1.3.0'}}
rootdir: C:\Users\yatyang\PycharmProjects\pytest_example, inifile:
plugins: metadata-1.3.0, html-1.14.2
collected 2 items
slowTest_print.py::test_1 PASSED
slowTest_print.py::test_2 FAILED
================================== FAILURES ===================================
___________________________________ test_2 ____________________________________
def test_2():
print 'in test_2'
time.sleep(1)
print 'after 1 sec'
time.sleep(1)
print 'after 2 sec'
time.sleep(1)
print 'after 3 sec'
> assert 0, 'failing for demo purposes'
E AssertionError: failing for demo purposes
E assert 0
slowTest_print.py:22: AssertionError
---------------------------- Captured stdout call -----------------------------
in test_2
after 1 sec
after 2 sec
after 3 sec
===================== 1 failed, 1 passed in 6.45 seconds ======================
C:\Users\yatyang\PycharmProjects\pytest_example>
我們可以用‘-s’參數或者 ‘–capture=no’,這樣就可以輸出所有測試用的print信息。但是pytest還是會等着所有的測試用例都執行完畢才會顯示運行結果。可以看到下面的test_1也顯示出print的相關信息。
C:\Users\yatyang\PycharmProjects\pytest_example>py.test --capture=no slowTest_print.py
============================= test session starts =============================
platform win32 -- Python 2.7.13, pytest-3.0.6, py-1.4.32, pluggy-0.4.0
metadata: {'Python': '2.7.13', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': {'py': '1.4.32', 'pytest': '3.0.6', 'pluggy': '0.4.0'}, 'JAVA_HOME': 'C:\\Program Files (x86)\\Java\\jd
k1.7.0_01', 'Plugins': {'html': '1.14.2', 'metadata': '1.3.0'}}
rootdir: C:\Users\yatyang\PycharmProjects\pytest_example, inifile:
plugins: metadata-1.3.0, html-1.14.2
collected 2 items
slowTest_print.py test_1
after 1 sec
after 2 sec
after 3 sec
.in test_2
after 1 sec
after 2 sec
after 3 sec
F
================================== FAILURES ===================================
___________________________________ test_2 ____________________________________
def test_2():
print 'in test_2'
time.sleep(1)
print 'after 1 sec'
time.sleep(1)
print 'after 2 sec'
time.sleep(1)
print 'after 3 sec'
> assert 0, 'failing for demo purposes'
E AssertionError: failing for demo purposes
E assert 0
slowTest_print.py:22: AssertionError
===================== 1 failed, 1 passed in 6.17 seconds ======================
2. Python Logging用法
一般情況下,一些程序的調試過程中我們會讓它輸出一些信息,特別是一些大型的程序,我們通過這些信息可以了解程序的運行情況,python提供了一個日志模塊logging,它可以把我們想要的信息全部保存到一個日志文件中,方便查看。
import logging
logging.debug('This is debug message')
logging.info('This is info message')
logging.warning('This is warning message')
屏幕上打印:
WARNING:root:This is warning message
默認情況下,logging將日志打印到屏幕,日志級別為WARNING;
日志級別大小關系為:CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET,當然也可以自己定義日志級別。
3. 在pytest中用logging代替print
我們現在來看看在pytest的測試用例里面用logging的輸出代替print,有什么不同。
slowTest_logging.py
import time
import logging
logging.basicConfig(level=logging.DEBUG)
def test_1():
log = logging.getLogger('test_1')
time.sleep(1)
log.debug('after 1 sec')
time.sleep(1)
log.debug('after 2 sec')
time.sleep(1)
log.debug('after 3 sec')
assert 1, 'should pass'
def test_2():
log = logging.getLogger('test_2')
time.sleep(1)
log.debug('after 1 sec')
time.sleep(1)
log.debug('after 2 sec')
time.sleep(1)
log.debug('after 3 sec')
assert 0, 'failing for demo purposes'
運行結果如下,log信息的顯示是不是可讀性更好了呢。可是pytest還是要等所有的結果都運行完畢才完全輸出到屏幕上,沒法看到實時的運行情況。比如現在要測試一個新的image,不知道quality如何,如果測試用例非常多,測試人員就得一直等,也許前面的一些測試用都失敗就可以停止執行了。那怎么實現實時顯示呢?請看方法4。
C:\Users\yatyang\PycharmProjects\pytest_example>pytest slowTest_logging.py
============================= test session starts =============================
platform win32 -- Python 2.7.13, pytest-3.0.6, py-1.4.32, pluggy-0.4.0
metadata: {'Python': '2.7.13', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': {'py': '1.4.32', 'pytest': '3.0.6', 'pluggy': '0.4.0'}, 'JAVA_HOME': 'C:\\Program Files (x86)\\Java\\jd
k1.7.0_01', 'Plugins': {'html': '1.14.2', 'metadata': '1.3.0'}}
rootdir: C:\Users\yatyang\PycharmProjects\pytest_example, inifile:
plugins: metadata-1.3.0, html-1.14.2
collected 2 items
slowTest_logging.py .F
================================== FAILURES ===================================
___________________________________ test_2 ____________________________________
def test_2():
log = logging.getLogger('test_2')
time.sleep(1)
log.debug('after 1 sec')
time.sleep(1)
log.debug('after 2 sec')
time.sleep(1)
log.debug('after 3 sec')
> assert 0, 'failing for demo purposes'
E AssertionError: failing for demo purposes
E assert 0
slowTest_logging.py:25: AssertionError
---------------------------- Captured stderr call -----------------------------
DEBUG:test_2:after 1 sec
DEBUG:test_2:after 2 sec
DEBUG:test_2:after 3 sec
===================== 1 failed, 1 passed in 6.37 seconds ======================
C:\Users\yatyang\PycharmProjects\pytest_example>pytest -s slowTest_logging.py
============================= test session starts =============================
platform win32 -- Python 2.7.13, pytest-3.0.6, py-1.4.32, pluggy-0.4.0
metadata: {'Python': '2.7.13', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': {'py': '1.4.32', 'pytest': '3.0.6', 'pluggy': '0.4.0'}, 'JAVA_HOME': 'C:\\Program Files (x86)\\Java\\jd
k1.7.0_01', 'Plugins': {'html': '1.14.2', 'metadata': '1.3.0'}}
rootdir: C:\Users\yatyang\PycharmProjects\pytest_example, inifile:
plugins: metadata-1.3.0, html-1.14.2
collected 2 items
slowTest_logging.py DEBUG:test_1:after 1 sec
DEBUG:test_1:after 2 sec
DEBUG:test_1:after 3 sec
.DEBUG:test_2:after 1 sec
DEBUG:test_2:after 2 sec
DEBUG:test_2:after 3 sec
F
================================== FAILURES ===================================
___________________________________ test_2 ____________________________________
def test_2():
log = logging.getLogger('test_2')
time.sleep(1)
log.debug('after 1 sec')
time.sleep(1)
log.debug('after 2 sec')
time.sleep(1)
log.debug('after 3 sec')
> assert 0, 'failing for demo purposes'
E AssertionError: failing for demo purposes
E assert 0
slowTest_logging.py:25: AssertionError
===================== 1 failed, 1 passed in 6.18 seconds ======================
4. pytest用logging和–capture=no實現實時輸出log信息
請自己去運行下面的程序吧,可以看到該程序是實時輸出當前測試用例執行的情況。
C:\Users\yatyang\PycharmProjects\pytest_example>pytest -s slowTest_logging.py
============================= test session starts =============================
platform win32 -- Python 2.7.13, pytest-3.0.6, py-1.4.32, pluggy-0.4.0
metadata: {'Python': '2.7.13', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': {'py': '1.4.32', 'pytest': '3.0.6', 'pluggy': '0.4.0'}, 'JAVA_HOME': 'C:\\Program Files (x86)\\Java\\jd
k1.7.0_01', 'Plugins': {'html': '1.14.2', 'metadata': '1.3.0'}}
rootdir: C:\Users\yatyang\PycharmProjects\pytest_example, inifile:
plugins: metadata-1.3.0, html-1.14.2
collected 2 items
slowTest_logging.py DEBUG:test_1:after 1 sec
DEBUG:test_1:after 2 sec
DEBUG:test_1:after 3 sec
.DEBUG:test_2:after 1 sec
DEBUG:test_2:after 2 sec
DEBUG:test_2:after 3 sec
F
================================== FAILURES ===================================
___________________________________ test_2 ____________________________________
def test_2():
log = logging.getLogger('test_2')
time.sleep(1)
log.debug('after 1 sec')
time.sleep(1)
log.debug('after 2 sec')
time.sleep(1)
log.debug('after 3 sec')
> assert 0, 'failing for demo purposes'
E AssertionError: failing for demo purposes
E assert 0
slowTest_logging.py:25: AssertionError
===================== 1 failed, 1 passed in 6.20 seconds ======================
5.總結
在寫自動化測試用例時,添加有用的log信息是非常有必要的。比如在初期的調試過程,能夠一旦運行有問題,就可以獲取到精確的調試信息。后期在穩定的運行中,其他測試人員來運行也可以很容易上手,所以大家一定要重視測試用例的調試信息。
通過本文,應該知道如何用pytest,logging和–capture=no實現運行測試用例的實時輸出所有的log信息。
3, 代碼覆蓋率-多用在單元測試中
一,上篇(---- pytest-cov)
簡介:
pytest-cov 是pytest的一個插件,其本質也是引用 python coverage 庫 用來統計代碼覆蓋率。以下這篇文章只供理解,真實項目的話,我們都是用api調用接口的,所以真實項目使用會更復雜一些,這個待下次說明。
另外說明:coverage 是在覆蓋率是語句覆蓋的一種,不能對你的邏輯做判讀,真實意義的話,需要多結合項目本身,這個覆蓋率數據沒有很強大說服力,不要盲目追求。
一般來說:
路徑覆蓋率 > 判定覆蓋 > 語句覆蓋
安裝
pip install pytest-cover
安裝完后有
py.test -h 可以看到多了以下的用法,說明安裝成功:
coverage reporting with distributed testing support:
范例
新建三個文件,cau.py 與test_conver.py 在同一個目錄code下。run.py文件在上一級目錄pp下。
代碼關系如下。
1.新建函數文件cau.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def cau (type,n1, n2):
if type==1:
a=n1 + n2
elif type==2:
a = n1 - n2
else:
a=n1 * n2
return a
2.新建test_conver.py測試文件:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from code.cau import cau
class Test_cover:
def test_add(self):
a=cau(1,2,3)
assert a==3
3.新建執行腳本run.py
#!/usr/bin/env ```python
# -*- coding: utf-8 -*-
import pytest
if __name__=='__main__':
pytest.main(["--cov=./code/" ,"--cov-report=html","--cov-config=./code/.coveragerc"] ) # 執行某個目錄下case
說明:–cov參數 后面接的是測試的目錄 (經給測試,不能指定某個特定的文件。),程序代碼跟測試腳本必須在同一個文件下。 --cov-report=html 生成報告 ,只需要python run.py 就可以運行
coveragerc 意思是跳過某些腳本的覆蓋率測試。此處跳過test_cover.py文件跟init文件。
內容如下:
[run]
omit =
tests/*
*/__init__.py
*/test_cover.py
結果
生成完后可以直接點擊indexhtml
可以看到如下的執行情況,綠色代表運行,紅色代表未被執行,自己檢查下代碼邏輯,可以得出該結果是正確的。
二:下篇(— coverage.py api)
使用pytest-cov 無法統計用 api調用服務的測試腳本所覆蓋率,但大部分的項目基本也是使用api調用。所以我們額外需要使用coverage.py api 來統計。
當你安裝pytest-cov時,已經默認安裝了coverage 這個庫。
服務啟動
要想掃描到代碼,必須在服務啟動的時候要插入coverage相關配置。
我這邊是flask 啟動的,所以在flask啟動的代碼上添加,如下:
if __name__ == '__main__':
cov = Coverage()
cov.start() # 開始檢測代碼
print ("qidong")
app.run(debug=True, host='0.0.0.0',port=9098) #原本只有這一行
cov.stop() # 停止紀錄
print ("guanbi")
cov.save() # 保存在 .coverage 中
print ("save")
cov.html_report() # 生成 HTML 報告
原本我們是python xx.py 這樣啟動,但現在不可以。
需要改成這樣,source 表示目錄,xx表示執行文件。
coverage run --source='/xxx/' xx.py
啟動運行圖如下:
然后調用你的自動化腳本(自動化腳本是直接調的該服務提供的api 。)
自動化如果正常運行,能看到運行的請求
以上說明你的腳本跟服務是沒問題的
ctr-c停掉該腳本后,最后顯示save,如果顯示”Coverage.py warning: No data was collected. (no-data-collected)“ 說明的服務運行方式有問題,coverage 服務沒有運行到你代碼
報告生成
輸入以下命令
coverage report
最后一步最后輸入
coverage html
這樣就可以省 html 文件了。
導出在window上看,具體點擊某個文件,點擊run,你可以看到綠色的就是運行的。但有問題是,你會發現有些代碼應該是要被執行,但卻沒有被執行。所以coverage的數據准不准很難說。
4,allure測試報告框架
pytest+allure現在都是結合jenkins來搞的,很簡單相信大家都會,不會的老哥可以去看我的另一個博客持續集成里的有寫
5,定制報告
定制報告
Feature: 標注主要功能模塊
Story: 標注Features功能模塊下的分支功能
Severity: 標注測試用例的重要級別
Step: 標注測試用例的重要步驟
Issue和TestCase: 標注Issue、Case,可加入URL
1、Features定制詳解
# -*- coding: utf-8 -*-
# @Time : 2018/8/17 上午10:10
# @Author : WangJuan
# @File : test_case.py
import allure
import pytest
@allure.feature('test_module_01')
def test_case_01():
""" 用例描述:Test case 01 """
assert 0
@allure.feature('test_module_02')
def test_case_02():
""" 用例描述:Test case 02 """
assert 0 == 0
if __name__ == '__main__':
pytest.main(['-s', '-q', '--alluredir', './report/xml'])
添加feature,Report展示見下圖。
2、Story定制詳解
# -*- coding: utf-8 -*-
# @Time : 2018/8/17 上午10:10
# @Author : WangJuan
# @File : test_case.py
import allure
import pytest
@allure.feature('test_module_01')
@allure.story('test_story_01')
def test_case_01():
""" 用例描述:Test case 01 """
assert 0
@allure.feature('test_module_01')
@allure.story('test_story_02')
def test_case_02():
""" 用例描述:Test case 02 """
assert 0 == 0
if __name__ == '__main__':
pytest.main(['-s', '-q', '--alluredir', './report/xml'])
添加story,Report展示見下圖。
3、用例標題和用例描述定制詳解
# -*- coding: utf-8 -*-
# @Time : 2018/8/17 上午10:10
# @Author : WangJuan
# @File : test_case.py
import allure
import pytest
@allure.feature('test_module_01')
@allure.story('test_story_01')
#test_case_01為用例title
def test_case_01():
""" 用例描述:這是用例描述,Test case 01,描述本人 """
#注釋為用例描述
assert 0
if __name__ == '__main__':
pytest.main(['-s', '-q', '--alluredir', './report/xml'])
添加用例標題和用例描述,Report展示見下圖。
4 、Severity定制詳解
Allure中對嚴重級別的定義:
1、 Blocker級別:中斷缺陷(客戶端程序無響應,無法執行下一步操作)
2、 Critical級別:臨界缺陷( 功能點缺失)
3、 Normal級別:普通缺陷(數值計算錯誤)
4、 Minor級別:次要缺陷(界面錯誤與UI需求不符)
5、 Trivial級別:輕微缺陷(必輸項無提示,或者提示不規范)
# -*- coding: utf-8 -*-
# @Time : 2018/8/17 上午10:10
# @Author : WangJuan
# @File : test_case.py
import allure
import pytest
@allure.feature('test_module_01')
@allure.story('test_story_01')
@allure.severity('blocker')
def test_case_01():
""" 用例描述:Test case 01 """
assert 0
@allure.feature('test_module_01')
@allure.story('test_story_01')
@allure.severity('critical')
def test_case_02():
""" 用例描述:Test case 02 """
assert 0 == 0
@allure.feature('test_module_01')
@allure.story('test_story_02')
@allure.severity('normal')
def test_case_03():
""" 用例描述:Test case 03 """
assert 0
@allure.feature('test_module_01')
@allure.story('test_story_02')
@allure.severity('minor')
def test_case_04():
""" 用例描述:Test case 04 """
assert 0 == 0
if __name__ == '__main__':
pytest.main(['-s', '-q', '--alluredir', './report/xml'])
添加Severity,Report展示見下圖。
5、Step定制詳解
# -*- coding: utf-8 -*-
# @Time : 2018/8/17 上午10:10
# @Author : WangJuan
# @File : test_case.py
import allure
import pytest
@allure.step("字符串相加:{0},{1}")
# 測試步驟,可通過format機制自動獲取函數參數
def str_add(str1, str2):
if not isinstance(str1, str):
return "%s is not a string" % str1
if not isinstance(str2, str):
return "%s is not a string" % str2
return str1 + str2
@allure.feature('test_module_01')
@allure.story('test_story_01')
@allure.severity('blocker')
def test_case():
str1 = 'hello'
str2 = 'world'
assert str_add(str1, str2) == 'helloworld'
if __name__ == '__main__':
pytest.main(['-s', '-q', '--alluredir', './report/xml'])
添加Step,Report展示見下圖。
6、Issue和TestCase定制詳解
# -*- coding: utf-8 -*-
# @Time : 2018/8/17 上午10:10
# @Author : WangJuan
# @File : test_case.py
import allure
import pytest
@allure.step("字符串相加:{0},{1}") # 測試步驟,可通過format機制自動獲取函數參數
def str_add(str1, str2):
print('hello')
if not isinstance(str1, str):
return "%s is not a string" % str1
if not isinstance(str2, str):
return "%s is not a string" % str2
return str1 + str2
@allure.feature('test_module_01')
@allure.story('test_story_01')
@allure.severity('blocker')
@allure.issue("http://www.baidu.com")
@allure.testcase("http://www.testlink.com")
def test_case():
str1 = 'hello'
str2 = 'world'
assert str_add(str1, str2) == 'helloworld'
if __name__ == '__main__':
pytest.main(['-s', '-q', '--alluredir', './report/xml'])
添加Issue和TestCase,Report展示見下圖。
8、attach定制詳解
file = open('../test.png', 'rb').read()
allure.attach('test_img', file, allure.attach_type.PNG)
在報告中增加附件:allure.attach(’arg1’,’arg2’,’arg3’):
arg1:是在報告中顯示的附件名稱
arg2:表示添加附件的內容
arg3:表示添加的類型(支持:HTML,JPG,PNG,JSON,OTHER,TEXTXML)
添加attach參數,Report展示見下圖。
6,pytest運行指定用例
隨着軟件功能的增加,模塊越來越多,也意味用例越來越多,為了節約執行時間,快速得到測試報告與結果,在工作中可以通過運行指定用例,達到快速執行用例
例子目錄
spec_sub1_modul_test.py
#coding: UTF-8
import pytest
def test_004_spec():
assert 1==1
def test_005_spec():
assert True==False
class Test_Class():
def test_006_spec(self):
assert 'G' in "Goods"
spec_sub2_modul_test.py
#coding: UTF-8
import pytest
def test_007_spec():
assert 1==1
def test_008_spec():
assert True==False
class Test_Class():
def test_009_spec(self):
assert 'G' in "Goods"
spec_001_modul_test
#coding: UTF-8
import pytest
def test_001_spec():
assert 1==1
def test_002_spec():
assert True==False
class Test_Class():
def test_003_spec(self):
assert 'H' in "Hell,Jerry"
運行指定模塊
if __name__ == '__main__':
pytest.main("-v -s spec_001_modul_test.py")
運行批量文件夾(運行當前文件夾包括子文件夾所有用例)
#coding: UTF-8
import pytest
if __name__ == '__main__':
pytest.main("-v -s ./")
運行指定文件夾(subpath1目錄下面所有用例)
#coding: UTF-8
import pytest
if __name__ == '__main__':
pytest.main("-v -s subpath1/")
運行模塊中指定用例 (運行模塊中test_001_spec用例)
if __name__ == '__main__':
pytest.main("-v -s spec_001_modul_test.py::test_001_spec")
運行class中指定的用例(運行模塊中Test_Class類test_003_spec方法)
if __name__ == '__main__':
pytest.main("-v -s spec_001_modul_test.py::Test_Class::test_003_spec")
模糊匹配運行用例(匹配當前目錄下面包含)
if __name__ == '__main__':
#運行spec_001_modul_test模塊中用例名稱包含spec的用例
pytest.main("-v -s -k spec spec_001_modul_test.py")
#運行當前文件夾匹配Test_Class的用例,類文件下面的用例
pytest.main('-s -v -k Test_Class')
7,按重要性級別進行一定范圍測試
此標記用來標識測試用例或者測試類的級別,分為blocker,critical,normal,minor,trivial5個級別,下面們把測試用例按級別標記,並查看一下測試報告
8, 為測試添加詳說明@allure.description;@allure.title;
1.title case標題
可以自定義用例標題,標題默認為函數名.
@allure.title
# -*- coding: utf-8 -*-
# @Time : 2019/3/12 11:46
# @Author : zzt
import allure
import pytest
@allure.title("用例標題0")
def test_0():
pass
@allure.title("用例標題1")
def test_1():
pass
def test_2():
pass
執行效果:
- 說明
可以添加測試的詳細說明,以便根據需要為報告閱讀器提供盡可能多的上下文。
兩種方式:@allure.description 提供描述字符串的裝飾器
@allure.description_html 提供一些HTML在測試用例的描述部分 (待研究)
# -*- coding: utf-8 -*-
# @Time : 2019/3/12 11:46
# @Author : zzt
import allure
import pytest
@allure.title("用例標題0")
@allure.description("這里是對test_0用例的一些詳細說明")
def test_0():
pass
@allure.title("用例標題1")
def test_1():
pass
@allure.title("用例標題2")
def test_2():
pass
9, 鏈接@allure.link @allure.issue @allure.testcase
@allure.link @allure.issue @allure.testcase
# -*- coding: utf-8 -*-
# @Time : 2019/3/12 11:46
# @Author : zzt
import allure
import pytest
@allure.feature('這里是一級標簽')
class TestAllure():
@allure.title("用例標題0")
@allure.story("這里是第一個二級標簽")
@pytest.mark.parametrize('param', ['青銅', '白銀', '黃金'])
def test_0(self, param):
allure.attach('附件內容是: '+param, '我是附件名', allure.attachment_type.TEXT)
@allure.title("用例標題1")
@allure.story("這里是第二個二級標簽")
def test_1(self):
allure.attach.file(r'E:\Myproject\pytest-allure\test\test_1.jpg', '我是附件截圖的名字', attachment_type=allure.attachment_type.JPG)
@allure.title("用例標題2")
@allure.story("這里是第三個二級標簽")
@allure.issue('http://baidu.com', name='點擊我跳轉百度')
@allure.testcase('http://bug.com/user-login-Lw==.html', name='點擊我跳轉禪道')
def test_2(self):
pass
執行結果如下:
七, 單元自動化測試pytest和allure在測試中應用 自動執行
1, 單元測試測試報告展示
2, conftest中編寫driver,范圍session,使用 addfinalizer在測試結束后關閉瀏覽器
3, 前端自動化測試-百度搜索功能實戰演示
報告可以展示許多不同類型的附件,用來補充測試,步驟等信息
allure.attach(body, name, attachment_type, extension)
body - 要寫入文件的原始內容。
name - 包含文件名的字符串
attachment_type- 其中一個allure.attachment_type值
extension - 提供的將用作創建文件的擴展名
或者 allure.attach.file(source, name, attachment_type, extension)
source - 包含文件路徑的字符串。
# -*- coding: utf-8 -*-
# @Time : 2019/3/12 11:46
# @Author : zzt
import allure
import pytest
@allure.feature('這里是一級標簽')
class TestAllure():
@allure.title("用例標題0")
@allure.story("這里是第一個二級標簽")
@pytest.mark.parametrize('param', ['青銅', '白銀', '黃金'])
def test_0(self, param):
allure.attach('附件內容是: '+param, '我是附件名', allure.attachment_type.TEXT)
@allure.title("用例標題1")
@allure.story("這里是第二個二級標簽")
def test_1(self):
allure.attach.file(r'E:\Myproject\pytest-allure\test\test_1.jpg', '我是附件截圖的名字', attachment_type=allure.attachment_type.JPG)
@allure.title("用例標題2")
@allure.story("這里是第三個二級標簽")
@allure.severity(allure.severity_level.NORMAL)
def test_2(self):
pass
執行結果如下:
4,源碼:Github:https://github.com/linda883/py_techDemo
5, CI/CD使用jenkins進行持續集成
在Jenkins集成相信大家都會就不講了,或者看我的持續集成博客