Allure-pytest功能特性介紹
前言
Allure框架是一個靈活的輕量級多語言測試報告工具,它不僅以web的方式展示了簡介的測試結果,而且允許參與開發過程的每個人從日常執行的測試中最大限度的提取有用資訊
從dev/qa的角度來看,Allure報告簡化了常見缺陷的統計:失敗的測試可以分為bug和被中斷的測試,還可以配置日誌、步驟、fixture、附件、計時、執行歷史以及與TMS和BUG管理系統整合,所以,通過以上配置,所有負責的開發人員和測試人員可以儘可能的掌握測試資訊。
從管理者的角度來看,Allure提供了一個清晰的“大圖”,其中包括已覆蓋的特性、缺陷聚集的位置、執行時間軸的外觀以及許多其他方便的事情。allure的模組化和可擴充套件性保證了您總是能夠對某些東西進行微調,使Allure更適合您,那麼今天我們就來說說如何使報告更加詳細的顯示我們需要的資訊,以及allure與jenkins的整合
生成報告
pytest框架編寫的專案如何生成測試報告,這裡將不再講解,具體過程可以參考:pytest進階之html測試報告
注意:python使用的allure外掛為allure-pytest
測試程式碼
為了大家能夠快速的認識allure的所有功能特性,附上完整的測試程式碼
""" ------------------------------------ @Time : 2019/8/28 19:50 @Auth : linux超 @File : conftest.py @IDE : PyCharm @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error! @QQ : [email protected] @GROUP: 878565760 ------------------------------------ """ import pytest import allure @pytest.mark.hookwrapper def pytest_runtest_makereport(item): outcome = yield report = outcome.get_result() report.nodeid = report.nodeid.encode("utf-8").decode("unicode_escape") # 解決亂碼 @allure.step("開啟瀏覽器") def fixture_step(): pass @pytest.fixture def init_url(): fixture_step() yield True
""" ------------------------------------ @Time : 2019/9/4 21:05 @Auth : linux超 @File : test_allure_feature.py @IDE : PyCharm @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error! @QQ : [email protected] @GROUP: 878565760 ------------------------------------ """ import pytest import allure import os def login(username=None, password=None): """模擬登入""" user = "linux超" pwd = "123456" if user == username and pwd == password: return {"code": 1001, "msg": "登入成功", "data": None} elif "" == password or password is None and username: return {"code": 1002, "msg": "密碼不能為空", "data": None} elif "" == username or username is None and password: return {"code": 1003, "msg": "使用者名稱不能為空", "data": None} else: return {"code": 1004, "msg": "使用者名稱或密碼錯誤", "data": None} @allure.step("輸入使用者名稱") def input_username(user): print("輸入使用者名稱") return user @allure.step("輸入密碼") def input_password(pwd): print("輸入密碼") return pwd login_success_data = [ # 測試資料 { "case": "使用者名稱正確, 密碼正確", "user": "linux超", "pwd": "123456", "expected": {"code": 1001, "msg": "登入成功", "data": None} } ] login_fail_data = [ { "case": "使用者名稱正確, 密碼為空", "user": "linux超", "pwd": "", "expected": {"code": 1002, "msg": "密碼不能為空", "data": None} }, { "case": "使用者名稱為空, 密碼正確", "user": "", "pwd": "linux超哥", "expected": {"code": 1003, "msg": "使用者名稱不能為空", "data": None} }, { "case": "使用者名稱錯誤, 密碼錯誤", "user": "linux", "pwd": "linux", "expected": {"code": 1004, "msg": "使用者名稱或密碼錯誤", "data": None} } ] username_none = [ { "case": "預設使用者名稱引數", "pwd": "123456", "expected": {"code": 1003, "msg": "使用者名稱不能為空", "data": None} } ] password_none = [ { "case": "預設密碼引數", "user": "linux超", "expected": {"code": 1002, "msg": "密碼不能為空", "data": None} } ] # 改變輸出結果 ids_login_success_data = [ "測試{}使用者名稱:{}密碼{}期望值{}". format(data["case"], data["user"], data["pwd"], data["expected"]) for data in login_success_data ] ids_login_fail_data = [ "測試{}使用者名稱:{}密碼{}期望值{}". format(data["case"], data["user"], data["pwd"], data["expected"]) for data in login_fail_data ] ids_username_none = [ "測試{}密碼{}期望值{}". format(data["case"], data["pwd"], data["expected"]) for data in username_none ] ids_password_none = [ "測試{}使用者名稱:{}期望值{}". format(data["case"], data["user"], data["expected"]) for data in password_none ] @allure.feature("登入模組") class TestLogin(object): @allure.severity(allure.severity_level.BLOCKER) @allure.story("測試登入成功") @allure.title("登入成功場景-{data}") @pytest.mark.parametrize("data", login_success_data, ids=ids_login_success_data) def test_login_success(self, data): """測試登入成功""" user = input_username(data["user"]) pwd = input_password(data["pwd"]) result = login(user, pwd) assert result == data["expected"] @allure.severity(allure.severity_level.CRITICAL) @allure.story("測試登入失敗") @pytest.mark.parametrize("data", login_fail_data, ids=ids_login_fail_data) def test_login_fail(self, data): """測試使用者名稱或密碼錯誤""" user = input_username(data["user"]) pwd = input_password(data["pwd"]) result = login(user, pwd) assert result == data["expected"] @allure.severity(allure.severity_level.MINOR) @allure.story("測試使用者名稱引數缺失") @pytest.mark.parametrize("data", username_none, ids=ids_username_none) def test_username_none(self, data): """測試預設使用者名稱""" pwd = input_password(data["pwd"]) result = login(password=pwd) assert result == data["expected"] @allure.severity(allure.severity_level.MINOR) @allure.story("測試密碼引數缺失") @pytest.mark.parametrize("data", password_none, ids=ids_password_none) def test_password_none(self, data): """測試預設密碼""" user = input_username(data["user"]) result = login(username=user) assert result == data["expected"] @allure.severity(allure.severity_level.MINOR) @allure.story("測試初始化地址") @allure.testcase("https://www.cnblogs.com/linuxchao/", "測試用例地址") def test_init_url(self, init_url): flag = init_url assert flag is True @allure.severity(allure.severity_level.NORMAL) @allure.story("測試失敗用例與用例中新增附件") @allure.link("https://www.cnblogs.com/linuxchao/", name="bug連結") @allure.description("這是一個一直執行失敗的測試用例") def test_failed(self): """你也可以在這裡新增用例的描述資訊,但是會被allure.description覆蓋""" try: assert False except AssertionError as e: with open("attach.png", "rb") as f: context = f.read() allure.attach(context, "錯誤圖片", attachment_type=allure.attachment_type.PNG) raise e @allure.severity(allure.severity_level.TRIVIAL) @allure.story("測試broken用例") @allure.issue("https://www.cnblogs.com/linuxchao/", "錯誤連結") def test_broken(self): """broken""" with open("broken.json", "r", encoding='utf8') as f: f.read() @allure.severity(allure.severity_level.TRIVIAL) @allure.story("測試無條件跳過測試用例") @pytest.mark.skip(reason="無條件跳過") def test_skip(self): """skip""" pass if __name__ == '__main__': pytest.main(["-vsq", "--alluredir", "./allure-results", ]) os.system(r"allure generate --clean ./allure-results -o ./allure-report")
""" ------------------------------------ @Time : 2019/8/28 19:45 @Auth : linux超 @File : test_allure_fixture.py @IDE : PyCharm @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error! @QQ : [email protected] @GROUP: 878565760 ------------------------------------ """ import pytest import os import allure def function_scope_step(): print("function_scope_step") def class_scope_step(): print("class_scope_step") def module_scope_step(): print("module_scope_step") def session_scope_step(): print("session_scope_step") def step_inside_test_body(): print("step_inside_test_body") @pytest.fixture(params=[True, False], ids=['param_true', 'param_false']) def function_scope_fixture_with_finalizer(request): if request.param: print('True') else: print('False') def function_scope_finalizer(): function_scope_step() request.addfinalizer(function_scope_finalizer) @pytest.fixture(scope='class') def class_scope_fixture_with_finalizer(request): def class_finalizer_fixture(): class_scope_step() request.addfinalizer(class_finalizer_fixture) @pytest.fixture(scope='module') def module_scope_fixture_with_finalizer(request): def module_finalizer_fixture(): module_scope_step() request.addfinalizer(module_finalizer_fixture) @pytest.fixture(scope='session') def session_scope_fixture_with_finalizer(request): def session_finalizer_fixture(): session_scope_step() request.addfinalizer(session_finalizer_fixture) @allure.severity(allure.severity_level.BLOCKER) @allure.feature("fixture場景") class TestClass(object): def test_with_scoped_finalizers(self, function_scope_fixture_with_finalizer, class_scope_fixture_with_finalizer, module_scope_fixture_with_finalizer, session_scope_fixture_with_finalizer): step_inside_test_body() if __name__ == '__main__': pytest.main(["-vsq", "--alluredir", "./allure-results", ]) os.system(r"allure generate --clean ./allure-results -o ./allure-report")test_allure_fixture.py
[ { "name": "Ignored tests", "matchedStatuses": ["skipped"] }, { "name": "Infrastructure problems", "matchedStatuses": ["broken", "failed"], "messageRegex": ".*bye-bye.*" }, { "name": "Outdated tests", "matchedStatuses": ["broken"], "traceRegex": ".*FileNotFoundException.*" }, { "name": "Product defects", "matchedStatuses": ["failed"] }, { "name": "Test defects", "matchedStatuses": ["broken"] } ]categories.json
Browser=Chrome Browser.Version=63.0 Stand=Production ApiUrl=127.0.0.1/login python.Version=3.6environment.properties
""" ------------------------------------ @Time : 2019/9/3 14:21 @Auth : linux超 @File : run.py @IDE : PyCharm @Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error! @QQ : [email protected] @GROUP: 878565760 ------------------------------------ """ import pytest import os if __name__ == '__main__': pytest.main(["-sq", "--alluredir", "./allure-results"]) os.system(r"allure generate --clean allure-results -o allure-report")run.py
目錄結構
Allure特性
Environment
在Allure報告中新增環境資訊,通過建立environment.properties或者environment.xml檔案,並把檔案存放到allure-results(這個目錄是生成最後的html報告之前,生成依賴檔案的目錄)目錄下
environment.properties
Browser=Chrome Browser.Version=63.0 Stand=Production ApiUrl=127.0.0.1/login python.Version=3.6
或者
environment.xml
<environment> <parameter> <key>Browser</key> <value>Chrome</value> </parameter> <parameter> <key>Browser.Version</key> <value>63.0</value> </parameter> <parameter> <key>Stand</key> <value>Production</value> </parameter> <parameter> <key>ApiUrl</key> <value>127.0.0.1/login</value> </parameter> <parameter> <key>python.Version</key> <value>3.6</value> </parameter> </environment>
執行run.py檢視報告
Categories
測試報告預設統計兩種型別的測試用例結果,失敗的用例和故障測試用例,我們可以自定義新增用例的統計型別,同樣需要在allure-results目錄下新建categories.json檔案
[ { "name": "Ignored tests", "matchedStatuses": ["skipped"] }, { "name": "Infrastructure problems", "matchedStatuses": ["broken", "failed"], "messageRegex": ".*bye-bye.*" }, { "name": "Outdated tests", "matchedStatuses": ["broken"], "traceRegex": ".*FileNotFoundException.*" }, { "name": "Product defects", "matchedStatuses": ["failed"] }, { "name": "Test defects", "matchedStatuses": ["broken"] } ]
執行run.py檢視報告
Fixtures and Finalizers
Fixtures和Finalizers是pytest在測試開始和測試結束呼叫的方法,allure會自動跟蹤每一個fixture的呼叫,並且詳細顯示會呼叫哪些fixture和引數,而且會保留正確的呼叫順數
測試程式碼
test_allure_html.py
def function_scope_step(): print("function_scope_step") def class_scope_step(): print("class_scope_step") def module_scope_step(): print("module_scope_step") def session_scope_step(): print("session_scope_step") def step_inside_test_body(): print("step_inside_test_body") @pytest.fixture(params=[True, False], ids=['param_true', 'param_false']) def function_scope_fixture_with_finalizer(request): if request.param: print('True') else: print('False') def function_scope_finalizer(): function_scope_step() request.addfinalizer(function_scope_finalizer) @pytest.fixture(scope='class') def class_scope_fixture_with_finalizer(request): def class_finalizer_fixture(): class_scope_step() request.addfinalizer(class_finalizer_fixture) @pytest.fixture(scope='module') def module_scope_fixture_with_finalizer(request): def module_finalizer_fixture(): module_scope_step() request.addfinalizer(module_finalizer_fixture) @pytest.fixture(scope='session') def session_scope_fixture_with_finalizer(request): def session_finalizer_fixture(): session_scope_step() request.addfinalizer(session_finalizer_fixture) class TestClass(object): def test_with_scoped_finalizers(self, function_scope_fixture_with_finalizer, class_scope_fixture_with_finalizer, module_scope_fixture_with_finalizer, session_scope_fixture_with_finalizer): step_inside_test_body()
執行run.py檢視報告
@allure.step
pytest支援使用@allure.step修飾某些測試用例中需要的函式,使測試用例在allure報告中能夠更加詳細的顯示測試過程
測試程式碼
test_allure_feature.py檔案中修改如下程式碼
@allure.step("輸入使用者名稱") def input_username(): print("輸入使用者名稱") @allure.step("輸入密碼") def input_password(): print("輸入密碼")
執行run.py檢視報告
conftest.py
@allure.step修飾的測試步驟還支援在conftest.py檔案中定義,作為fixture的步驟,現在我們在專案目錄下新建conftest.py檔案,寫入如下程式碼
conftest.py
@allure.step("開啟瀏覽器") def fixture_step(): pass @pytest.fixture def init_url(): fixture_step() yield True
test_allure_feature.py檔案中新增如下用例
def test_init_url(self, init_url): flag = init_url assert flag == True
執行run.py檢視報告
allure.attach
使用allure.attach可以給報告中新增檔案,圖片,log,html程式碼等等。 我們修改test_allure_feature.py中如下用例, 並在用例所在目錄新增attach.png圖片
def test_failed(self): """failed""" try: assert False except AssertionError as e: with open("attach.png", "rb") as f: context = f.read() allure.attach(context, "錯誤圖片", attachment_type=allure.attachment_type.PNG) raise e
執行run.py檢視報告
@allure.description
如果你想在報告中展示測試用例的描述資訊,那麼你可以使用@allure.description(string)或者@allure.description_html(html程式碼)修飾你的測試用例,test_allure_feature.py檔案修改如下程式碼
@allure.description("這是一個一直執行失敗的測試用例") def test_failed(self): """你也可以在這裡新增用例的描述資訊,但是會被allure.description覆蓋""" try: assert False except AssertionError as e: with open("attach.png", "rb") as f: context = f.read() allure.attach(context, "錯誤圖片", attachment_type=allure.attachment_type.PNG) raise e
執行run.py檢視報告
@allure.title
使用allure.title(title)可以重新命名測試用例在allure報告中的名稱,test_allure_feature.py檔案修改如下程式碼
@allure.title("登入成功場景-{data}") @pytest.mark.parametrize("data", login_success_data, ids=ids_login_success_data) def test_login_success(self, data): """測試登入成功""" user = input_username(data["user"]) pwd = input_password(data["pwd"]) result = login(user, pwd) assert result == data["expected"]
@allure.link
@allure.testcase
@allure.issue
這三種特性都可以給測試用例新增一個連結,test_allure_feature.py檔案修改如下程式碼
@allure.testcase("https://www.cnblogs.com/linuxchao/", "測試用例地址") def test_init_url(self, init_url): flag = init_url assert flag == True @allure.link("https://www.cnblogs.com/linuxchao/", name="bug連結") @allure.description("這是一個一直執行失敗的測試用例") def test_failed(self): """你也可以在這裡新增用例的描述資訊,但是會被allure.description覆蓋""" try: assert False except AssertionError as e: with open("attach.png", "rb") as f: context = f.read() allure.attach(context, "錯誤圖片", attachment_type=allure.attachment_type.PNG) raise e @allure.issue("https://www.cnblogs.com/linuxchao/", "錯誤連結") def test_broken(self): """broken""" with open("broken.json", "r", encoding='utf8') as f: f.read()
執行run.py檢視報告
@allure.feature
@allure.story
feature和story被稱為行為驅動標記,因為使用這個兩個標記,通過報告可以更加清楚的掌握每個測試用例的功能和每個測試用例的測試場景
在test_allure_feature.py檔案中的測試類使用@allure.feature修飾, 測試方法使用@allure.story修飾
執行run.py檢視報告
以上兩種標記不僅僅能夠在測試報告中顯示,而且還可以使用命令執行指定的測試模組或者場景
@allure.severity
此標記用來標識測試用例或者測試類的級別,分為blocker,critical,normal,minor,trivial5個級別,下面們把測試用例按級別標記,並檢視一下測試報告
總結
以上就是所有的allure-pytest外掛在pytest中支援的大部分功能特性,也許整理的不是很詳細,所以如果你想詳細的瞭解具體的特性在報告中的效果,還需自己動手嘗試一下,附上本文參考連結
https://docs.qameta.io/all