[pytest] 配置

這里寫目錄標題

  • Pytest
    • Init
    • Run
      • 3. 根據命令行選項將不同的值傳遞給測試函數
    • Report
      • 1. 向測試報告標題添加信息
      • 2. 分析測試持續時間 `pytest --durations=3`
      • 3. 增量測試 - 測試步驟
      • --junitxml={report}.xml
        • 1. testsuite
          • 1.1 在測試套件級別添加屬性節點 record_testsuite_property
        • 2. testcase
          • 2.1 記錄測試的其他信息 record_property
          • 2.2 向testcase元素添加額外的xml屬性 record_xml_attribute
    • Hooks
    • other plugin 好玩好用的

Pytest

Init

Run

  1. 更改配置pytest.ini與項目同級
# content of pytest.ini
# Example 1: have pytest look for "check" instead of "test"
[pytest]
;更改目錄遞歸
norecursedirs = .svn _build tmp*;更改命名約定
python_files = check_*.py
python_classes = Check
python_functions = *_check
;可以通過在模式之間添加空格來檢查多個 glob 模式
;python_files = test_*.py example_*.py;將命令行參數解釋
;addopts = --tb=short
;addopts = --pyargs
;export PYTEST_ADDOPTS="-v"
addopts = -vv --html-report=report.html
  1. 引進@pytest.mark.parametrize中ids導致編碼亂碼
def pytest_collection_modifyitems(items):for item in items:item.name = item.name.encode('utf-8').decode('unicode-escape')item._nodeid = item._nodeid.encode('utf-8').decode('unicode-escape')
  1. 定義自己對失敗斷言的解釋 pytest_assertrepr_compare(config, op, left, right)

    • config (Config) – The pytest config object.
    • op (str) – The operator, e.g. “==”, “!=”, “not in”.
    • left (object) – The left operand.
    • right (object) – The right operand.
# content of conftest.py
from test_foocompare import Foodef pytest_assertrepr_compare(op, left, right):if isinstance(left, Foo) and isinstance(right, Foo) and op == "==":return ["Comparing Foo instances:",f"   vals: {left.val} != {right.val}",]
# content of test_foocompare.py
class Foo:def __init__(self, val):self.val = valdef __eq__(self, other):return self.val == other.valdef test_compare():f1 = Foo(1)f2 = Foo(2)assert f1 == f2

output

$ pytest -q test_foocompare.py
F                                                                    [100%]
================================= FAILURES =================================
_______________________________ test_compare _______________________________def test_compare():f1 = Foo(1)f2 = Foo(2)
>       assert f1 == f2
E       assert Comparing Foo instances:
E            vals: 1 != 2test_foocompare.py:12: AssertionError
========================= short test summary info ==========================
FAILED test_foocompare.py::test_compare - assert Comparing Foo instances:
1 failed in 0.12s

3. 根據命令行選項將不同的值傳遞給測試函數

# content of conftest.py
import pytestdef pytest_addoption(parser):parser.addoption("--cmdopt", action="store", default="type1", help="my option: type1 or type2")@pytest.fixture
def cmdopt(request):return request.config.getoption("--cmdopt")
# content of test_sample.py
def test_answer(cmdopt):if cmdopt == "type1":print("first")elif cmdopt == "type2":print("second")assert 0  # to see what was printed

output

# ************
# 沒有提供參數
# ************
$ pytest -q test_sample.py 
F                                                                    [100%]
================================= FAILURES =================================
_______________________________ test_answer ________________________________cmdopt = 'type1'def test_answer(cmdopt):if cmdopt == "type1":print("first")elif cmdopt == "type2":print("second")
>       assert 0  # to see what was printed
E       assert 0test_sample.py:6: AssertionError
--------------------------- Captured stdout call ---------------------------
first
========================= short test summary info ==========================
FAILED test_sample.py::test_answer - assert 0
1 failed in 0.12s# ************
# 提供參數
# ************
$ pytest -q --cmdopt=type2
F                                                                    [100%]
================================= FAILURES =================================
_______________________________ test_answer ________________________________cmdopt = 'type2'def test_answer(cmdopt):if cmdopt == "type1":print("first")elif cmdopt == "type2":print("second")
>       assert 0  # to see what was printed
E       assert 0test_sample.py:6: AssertionError
--------------------------- Captured stdout call ---------------------------
second
========================= short test summary info ==========================
FAILED test_sample.py::test_answer - assert 0
1 failed in 0.12s
  • 如果需要更詳細信息
# content of conftest.py
import pytestdef type_checker(value):msg = "cmdopt must specify a numeric type as typeNNN"if not value.startswith("type"):raise pytest.UsageError(msg)try:int(value[4:])except ValueError:raise pytest.UsageError(msg)return valuedef pytest_addoption(parser):parser.addoption("--cmdopt",action="store",default="type1",help="my option: type1 or type2",type=type_checker,)

output

$ pytest -q --cmdopt=type3
ERROR: usage: pytest [options] [file_or_dir] [file_or_dir] [...]
pytest: error: argument --cmdopt: invalid choice: 'type3' (choose from 'type1', 'type2')

Report

1. 向測試報告標題添加信息

1.1

# content of conftest.pydef pytest_report_header(config):return "project deps: mylib-1.1"

output

$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
project deps: mylib-1.1
rootdir: /home/sweet/project
collected 0 items========================== no tests ran in 0.12s ===========================

1.2 返回字符串列表,這些字符串將被視為多行信息

# content of conftest.pydef pytest_report_header(config):if config.get_verbosity() > 0:return ["info1: did you know that ...", "did you?"]

output 僅在使用“-v”運行時才會添加信息

$ pytest -v
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: .pytest_cache
info1: did you know that ...
did you?
rootdir: /home/sweet/project
collecting ... collected 0 items========================== no tests ran in 0.12s ===========================

2. 分析測試持續時間 pytest --durations=3

# content of test_some_are_slow.py
import timedef test_funcfast():time.sleep(0.1)def test_funcslow1():time.sleep(0.2)def test_funcslow2():time.sleep(0.3)

output

$ pytest --durations=3
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 3 itemstest_some_are_slow.py ...                                            [100%]=========================== slowest 3 durations ============================
0.30s call     test_some_are_slow.py::test_funcslow2
0.20s call     test_some_are_slow.py::test_funcslow1
0.10s call     test_some_are_slow.py::test_funcfast
============================ 3 passed in 0.12s =============================

3. 增量測試 - 測試步驟

如果前置步驟其中一個步驟失敗,則后續步驟將預期失敗。

# content of conftest.pyfrom typing import Dict, Tupleimport pytest# store history of failures per test class name and per index in parametrize (if parametrize used)
_test_failed_incremental: Dict[str, Dict[Tuple[int, ...], str]] = {}def pytest_runtest_makereport(item, call):if "incremental" in item.keywords:# incremental marker is usedif call.excinfo is not None:# the test has failed# retrieve the class name of the testcls_name = str(item.cls)# retrieve the index of the test (if parametrize is used in combination with incremental)parametrize_index = (tuple(item.callspec.indices.values())if hasattr(item, "callspec")else ())# retrieve the name of the test functiontest_name = item.originalname or item.name# store in _test_failed_incremental the original name of the failed test_test_failed_incremental.setdefault(cls_name, {}).setdefault(parametrize_index, test_name)def pytest_runtest_setup(item):if "incremental" in item.keywords:# retrieve the class name of the testcls_name = str(item.cls)# check if a previous test has failed for this classif cls_name in _test_failed_incremental:# retrieve the index of the test (if parametrize is used in combination with incremental)parametrize_index = (tuple(item.callspec.indices.values())if hasattr(item, "callspec")else ())# retrieve the name of the first test function to fail for this class name and indextest_name = _test_failed_incremental[cls_name].get(parametrize_index, None)# if name found, test has failed for the combination of class name & test nameif test_name is not None:pytest.xfail(f"previous test failed ({test_name})")
# content of test_step.pyimport pytest@pytest.mark.incremental
class TestUserHandling:def test_login(self):passdef test_modification(self):assert 0def test_deletion(self):passdef test_normal():pass

output

$ pytest -rx
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 4 itemstest_step.py .Fx.                                                    [100%]================================= FAILURES =================================
____________________ TestUserHandling.test_modification ____________________self = <test_step.TestUserHandling object at 0xdeadbeef0001>def test_modification(self):
>       assert 0
E       assert 0test_step.py:11: AssertionError
========================= short test summary info ==========================
XFAIL test_step.py::TestUserHandling::test_deletion - reason: previous test failed (test_modification)
================== 1 failed, 2 passed, 1 xfailed in 0.12s ==================

–junitxml={report}.xml

https://docs.pytest.org/en/stable/reference/reference.html
pytest v6.0+ 默認 xunit2 不支持 testcase添加屬性
建議設置

  • pytest.ini中配置junit_family=xunit1
  • pytest -o junit_family=xunit1
<?xml version="1.0" encoding="utf-8"?>
<testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="2" time="0.113"timestamp="2025-03-10T14:53:08.040765+08:00" hostname="Ding-Perlis-MP1Y70F1"><testcase classname="set_classname" name="set_name" file="test_case.py" line="49" time="0.006"/><testcase classname="set_classname" name="set_name" file="test_case.py" line="49" time="0.001"/></testsuite>
</testsuites>
1. testsuite
1.1 在測試套件級別添加屬性節點 record_testsuite_property

支持xunit2

import pytest@pytest.fixture(scope="session", autouse=True)
def log_global_env_facts(record_testsuite_property):record_testsuite_property("ARCH", "PPC")record_testsuite_property("STORAGE_TYPE", "CEPH")class TestMe:def test_foo(self):assert True

output


<testsuite errors="0" failures="0" name="pytest" skipped="0" tests="1" time="0.006"><properties><property name="ARCH" value="PPC"/><property name="STORAGE_TYPE" value="CEPH"/></properties><testcase classname="test_me.TestMe" file="test_me.py" line="16" name="test_foo" time="0.000243663787842"/>
</testsuite>
2. testcase
2.1 記錄測試的其他信息 record_property

請注意,使用此功能將中斷對最新JUnitXML架構的架構驗證。當與某些CI服務器一起使用時,這可能是一個問題

  • 方法一 test_case.py
def test_function(record_property):record_property("example_key", 1)assert True
  • 方法二 contest.py
# content of conftest.pydef pytest_collection_modifyitems(session, config, items):for item in items:for marker in item.iter_markers(name="test_id"):test_id = marker.args[0]item.user_properties.append(("test_id", test_id))# content of test_function.py
import pytest@pytest.mark.test_id(1501)
def test_function():assert True

output


<templt><testcase classname="test_function" file="test_function.py" line="0" name="test_function" time="0.0009"><properties><property name="example_key" value="1"/></properties></testcase><testcase classname="test_function" file="test_function.py" line="0" name="test_function" time="0.0009"><properties><property name="test_id" value="1501"/></properties></testcase>
</templt>
2.2 向testcase元素添加額外的xml屬性 record_xml_attribute

record_xml_attribute 是一個實驗性的特性,它的接口在未來的版本中可能會被更強大和通用的東西所取代。然而,功能本身將保持不變
請注意,使用此功能將中斷對最新JUnitXML架構的架構驗證。當與某些CI服務器一起使用時,這可能是一個問題

  • 方法一 test_case.py
import pytest@pytest.mark.parametrize("case", ["case1", "case2"])
def test_case(case, record_xml_attribute):record_xml_attribute('classname', 'set_classname')  # 重寫 valuerecord_xml_attribute('name', 'set_name')  # 重寫 valuerecord_xml_attribute('index', '123')  # 新增 key, valueprint("hello world")assert True
  • 方法二 contest.py
# edit to contest.py
import pytest@pytest.fixture(autouse=True)
def record_index(record_xml_attribute):record_xml_attribute('index', '123')  # 新增 key, value
  • output
<?xml version="1.0" encoding="utf-8"?>
<testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="2" time="0.113"timestamp="2025-03-10T14:53:08.040765+08:00" hostname="Ding-Perlis-MP1Y70F1"><testcase classname="set_classname" name="set_name" file="test_case.py" line="49" index="123" time="0.006"><system-out>hello world</system-out></testcase><testcase classname="set_classname" name="set_name" file="test_case.py" line="49" index="123" time="0.001"/></testsuite>
</testsuites>

Hooks

other plugin 好玩好用的

  • pytest_html_merger https://github.com/akavbathen/pytest_html_merger
    pip install pytest_html_merger 合并pytest_html報告
    export PATH="$HOME/.lcoal/bin:$PATH"
    pytest_html_merger -i /path/to/your/html/reports -o /path/to/output/report/merged.html
    
  • pytest-tally https://github.com/jeffwright13/pytest-tally
    pip install pytest-tally可在控制臺、應用程序或瀏覽器中顯示測試運行進度
    cd project # 與main.py同級
    python main.py
    pytest xxx# tally
    tally-rich
    tally-flask
    tally-tk
    
    https://github.com/jeffwright13/pytest-tally
  • pytest-sugarhttps://pypi.org/project/pytest-sugar/
    pip install pytest-sugar改變 pytest 的默認外觀(例如進度條、立即顯示失敗的測試)
    https://pypi.org/project/pytest-sugar/

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/897478.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/897478.shtml
英文地址,請注明出處:http://en.pswp.cn/news/897478.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Manus聯創澄清:我們并未使用MCP技術

摘要 近日&#xff0c;Manus聯創針對外界關于其產品可能涉及“沙盒越獄”的疑問進行了正式回應。公司明確表示并未使用Anthropic的MCP&#xff08;模型上下文協議&#xff09;技術&#xff0c;并強調MCP是一個旨在標準化應用程序與大型語言模型&#xff08;LLM&#xff09;之間…

初始化E9環境,安裝Sqlserver數據庫

title: 初始化E9環境,安裝Sqlserver數據庫 date: 2025-03-10 19:27:19 tags: E9SqlServer初始化E9環境,安裝Sqlserver數據庫 安裝E9本地環境安裝Sql server 數據庫1、檢查SQL Server服務是否開啟2、檢查SQL Server網絡網絡配置是否開啟創建一個ecology數據庫點擊初始化數據庫…

推薦一個基于Koin, Ktor Paging等組件的KMM Compose Multiplatform項目

Kotlin Multiplatform Mobile&#xff08;KMM&#xff09;已經從一個雄心勃勃的想法發展成為一個穩定而強大的框架&#xff0c;為開發人員提供了在多個平臺上無縫共享代碼的能力。通過最近的穩定版本里程碑&#xff0c;KMM已成為跨平臺開發領域的改變者。 環境設置 帶有Kotli…

在WSL2-Ubuntu中安裝CUDA12.8、cuDNN、Anaconda、Pytorch并驗證安裝

#記錄工作 提示&#xff1a;整個過程最好先開啟系統代理&#xff0c;也可以用鏡像源&#xff0c;確保有官方發布的最新特性和官方庫的完整和兼容性支持。 期間下載會特別慢&#xff0c;需要在系統上先開啟代理&#xff0c;然后WSL設置里打開網絡模式“Mirrored”,以設置WSL自動…

Ubuntu 24.04.2 允許 root 登錄桌面、 ssh 遠程、允許 Ubuntu 客戶機與主機拖拽傳遞文件

允許 root 登錄桌面 修改 /etc/pam.d/gdm-autologin , /etc/pam.d/gdm-password 加 # 以注釋掉 auth required pam_succeed_if.so user ! root quiet_success 允許 root 通過 ssh 登錄 修改 /etc/ssh/sshd_config ... #PermitRootLogin prohibit-password PermitRootLogin …

SQLAlchemy系列教程:如何執行原生SQL

Python中的數據庫交互提供了高級API。但是&#xff0c;有時您可能需要執行原始SQL以提高效率或利用數據庫特定的特性。本指南介紹在SQLAlchemy框架內執行原始SQL。 在SQLAlchemy中執行原生SQL SQLAlchemy雖然以其對象-關系映射&#xff08;ORM&#xff09;功能而聞名&#xff…

基于SpringBoot的手機銷售網站設計與實現(源碼+SQL腳本+LW+部署講解等)

專注于大學生項目實戰開發,講解,畢業答疑輔導&#xff0c;歡迎高校老師/同行前輩交流合作?。 技術范圍&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬蟲、數據可視化、安卓app、大數據、物聯網、機器學習等設計與開發。 主要內容&#xff1a;…

瀏覽器安全問題

1. XSS攻擊 概念 XSS 攻擊指的是跨站腳本攻擊&#xff0c;是一種代碼注入攻擊。攻擊者通過在網站注入惡意腳本&#xff0c;使之在用戶的瀏覽器上運行&#xff0c;從而盜取用戶的信息如 cookie 等 XSS本質是因為網站沒有對惡意代碼進行過濾&#xff0c;與正常代碼混合在一起了…

Spring(五)容器-依賴注入的三種方式

目錄 總結&#xff1a;通用的三種注入方式 1 字段注入 2 構造器注入 3 set注入 總結&#xff1a;通用的三種注入方式 優先使用構造器注入謹慎使用 Setter 注入避免濫用字段注入 通過構造器傳入依賴&#xff0c;確保對象創建時即完成初始化。通過 Setter 方法注入依賴&#x…

Python貝殼網二手小區數據爬取(2025年3月更)

文章目錄 一、代碼整體架構解析二、各部分代碼詳解1. main()主函數解析2. 會話初始化&#xff08;偽裝瀏覽器身份&#xff09;3. 動態參數生成&#xff08;反爬蟲核心機制&#xff09;4. 列表頁抓取&#xff08;獲取小區列表&#xff09;5. 列表頁解析&#xff08;提取小區信息…

使用服務器搭建一個專屬的密碼管理工具Vaultwarden

一、服務器配置與Docker環境 ?實例選型與系統準備? ?推薦配置?&#xff1a;?1核2GB內存?&#xff08;萊卡云L1型實例&#xff09;&#xff0c;Vaultwarden資源占用低&#xff0c;適合輕量級部署?34。?操作系統?&#xff1a;選擇 ?Ubuntu 22.04 LTS?&#xff0c;兼容…

安孚科技攜手政府產業基金、高能時代發力固態電池,開辟南孚電池發展新賽道

安孚科技出手&#xff0c;發力固態電池。 3月7日晚間&#xff0c;安孚科技&#xff08;603031.SH&#xff09;發布公告稱&#xff0c;公司控股子公司南孚電池擬與南平市綠色產業投資基金有限公司&#xff08;下稱“南平綠色產業基金”&#xff09;、高能時代&#xff08;廣東橫…

IO學習---->線程

1.創建兩個線程&#xff0c;分支線程1拷貝文件的前一部分&#xff0c;分支線程2拷貝文件的后一部分 #include <head.h> sem_t sem; long half_size 0; // 全局變量&#xff0c;供所有線程共享void* product(void *arg) {FILE *src fopen("IO.text", "…

深度學習分詞器char-level實戰詳解

一、三種分詞器基本介紹 word-level&#xff1a;將文本按照空格或者標點分割成單詞&#xff0c;但是詞典大小太大 subword-level&#xff1a;詞根分詞&#xff08;主流&#xff09; char-level&#xff1a;將文本按照字母級別分割成token 二、charlevel代碼 導包&#xff1…

基于SpringBoot實現旅游酒店平臺功能六

一、前言介紹&#xff1a; 1.1 項目摘要 隨著社會的快速發展和人民生活水平的不斷提高&#xff0c;旅游已經成為人們休閑娛樂的重要方式之一。人們越來越注重生活的品質和精神文化的追求&#xff0c;旅游需求呈現出爆發式增長。這種增長不僅體現在旅游人數的增加上&#xff0…

git規范提交之commitizen conventional-changelog-cli 安裝

一、引言 使用規范的提交信息可以讓項目更加模塊化、易于維護和理解&#xff0c;同時也便于自動化工具&#xff08;如發布工具或 Changelog 生成器&#xff09;解析和處理提交記錄。 通過編寫符合規范的提交消息&#xff0c;可以讓團隊和協作者更好地理解項目的變更歷史和版本…

前端實現版本更新自動檢測?

&#x1f916; 作者簡介&#xff1a;水煮白菜王&#xff0c;一位資深前端勸退師 &#x1f47b; &#x1f440; 文章專欄&#xff1a; 前端專欄 &#xff0c;記錄一下平時在博客寫作中&#xff0c;總結出的一些開發技巧和知識歸納總結?。 感謝支持&#x1f495;&#x1f495;&a…

硬件基礎(4):(5)設置ADC電壓采集中MCU的參考電壓

Vref 引腳通常是 MCU (特別是帶有 ADC 的微控制器) 上用來提供或接收基準電壓的引腳&#xff0c;ADC 會以該基準電壓作為量程參考對輸入模擬信號進行數字化轉換。具體來說&#xff1a; 命名方式 在不同廠家的 MCU 中&#xff0c;Vref 引腳可能會被標記為 VREF / VREF- / VREF_…

postman接口請求中的 Raw是什么

前言 在現代的網絡開發中&#xff0c;API 的使用已經成為數據交換的核心方式之一。然而&#xff0c;在與 API 打交道時&#xff0c;關于如何發送請求體&#xff08;body&#xff09;內容類型的問題常常困擾著開發者們&#xff0c;尤其是“raw”和“json”這兩個術語之間的區別…

為什么要使用前綴索引,以及建立前綴索引:sql示例

背景&#xff1a; 你想啊&#xff0c;數據庫里有些字段&#xff0c;它老長了&#xff0c;就像那種 varchar(255) 的字段&#xff0c;這玩意兒要是整個字段都拿來建索引&#xff0c;那可太占地方了。打個比方&#xff0c;這就好比你要在一個超級大的筆記本上記東西&#xff0c;每…