Pytest 框架執行用例流程淺談

来源:https://www.cnblogs.com/beyond-tester/archive/2023/08/29/17665993.html
-Advertisement-
Play Games

背景: 根據以下簡單的代碼示例,我們將從源碼的角度分析其中的關鍵載入執行步驟,對pytest整體流程架構有個初步學習。 代碼示例: import pytest def test_add(): assert 1 + 1 == 2 def test_sub(): assert 2 - 1 == 1 通過 ...


背景:

  根據以下簡單的代碼示例,我們將從源碼的角度分析其中的關鍵載入執行步驟,對pytest整體流程架構有個初步學習。

代碼示例:

import pytest

def test_add():

assert 1 + 1 == 2

def test_sub():

assert 2 - 1 == 1

  通過 pytest test_example.py 運行此代碼示例後,會觸發pytest的入口函數main(),這個函數定義在src/pytest/__main__.py中,它的作用是創建一個PytestConfig對象,並調用其

do_configure()和do_main()方法。PytestConfig對象是pytest的核心配置類,它負責解析命令行參數、讀取配置文件、註冊插件、創建Session對象等。PytestConfig對象定義在

src/_pytest/config/__init__.py中,它繼承了pluggy.HookimplMarker類,也就是說它可以作為一個插件管理器,調用各種hook函數。

```python
# src/pytest/__main__.py

def main():
    # 創建PytestConfig對象
    config = PytestConfig()
    # 調用config.do_configure()方法
    config.do_configure()
    # 調用config.do_main()方法
    config.do_main()
```

```python
# src/_pytest/config/__init__.py

class PytestConfig(pluggy.HookimplMarker):
    def __init__(self):
        # 解析命令行參數
        self.parse_args()
        # 讀取配置文件
        self.read_config_files()
        # 註冊插件
        self.register_plugins()
        # 創建Session對象
        self.session = Session(self)
    
    def do_configure(self):
        # 調用hook函數pytest_configure
        self.hook.pytest_configure(config=self)
    
    def do_main(self):
        # 調用hook函數pytest_sessionstart
        self.hook.pytest_sessionstart(session=self.session)
        # 調用Session對象的main()方法
        self.session.main()
```
 

  Session對象是pytest的核心上下文類,它負責管理整個測試過程的信息,包括收集測試用例、執行測試用例、生成測試報告等。Session對象定義在src/_pytest/main.py中,它繼承了

Collector類,也就是說它可以作為一個測試用例收集器。Session對象的main()方法是執行測試用例的主要入口,它會調用perform_collect()方法來收集測試用例,並返回一個列表items;然後

調用runtestloop()方法來迴圈執行items中的每個Item對象;最後調用hook函數pytest_sessionfinish來結束測試會話,並返回一個退出碼exitstatus。

```python
# src/_pytest/main.py

class Session(Collector):
    def __init__(self, config):
        # 初始化一些屬性和狀態信息
    
    def main(self):
        # 調用perform_collect()方法收集測試用例,並返回items列表
        items = self.perform_collect()
        # 調用runtestloop()方法迴圈執行items中的每個Item對象,並返回退出碼exitstatus
        exitstatus = self.runtestloop(items)
        # 調用hook函數pytest_sessionfinish來結束測試會話,並返回退出碼exitstatus
        self.hook.pytest_sessionfinish(session=self, exitstatus=exitstatus)
        return exitstatus
    
    def perform_collect(self):
        # 調用hook函數pytest_collectstart表示開始收集測試用例
        self.hook.pytest_collectstart(collector=self)
        # 調用自身的collect()方法來遞歸遍歷指定的測試文件或目錄,並返回一個列表items
        items = self.collect()
        # 調用hook函數pytest_collectreport表示收集測試用例結束,並生成收集報告
        self.hook.pytest_collectreport(report=CollectReport(self, "passed", items))
        # 調用hook函數pytest_collection_modifyitems允許對收集到的Item對象進行修改
        self.hook.pytest_collection_modifyitems(session=self, config=self.config, items=items)
        # 調用hook函數pytest_deselected表示從收集到的Item對象中篩選出需要執行的Item對象
        self.hook.pytest_deselected(items=self.deselected)
        # 調用hook函數pytest_collection_finish表示收集和篩選測試用例完成,並返回最終要執行的Item對象列表
        self.hook.pytest_collection_finish(session=self)
        return items
    
    def runtestloop(self, items):
        # 調用hook函數pytest_runtestloop表示開始迴圈執行測試用例
        self.hook.pytest_runtestloop(session=self)
        # 遍歷items列表,依次取出每個Item對象
        for item in items:
            # 調用Item對象的runtestprotocol()方法來執行單個測試用例的協議
            item.runtestprotocol()
        # 返回退出碼0表示成功
        return 0
```

  

  Item對象是pytest的核心測試類,它負責封裝和執行單個測試用例的信息,包括名稱、位置、參數化信息、標記信息等。Item對象定義在src/_pytest/python.py中,它繼承了Node類,也

就是說它可以作為一個測試節點。Item對象的runtestprotocol()方法是執行單個測試用例的主要入口,它會調用hook函數pytest_runtest_logstart來開始記錄日誌信息;然後調用runtest()方法

來執行測試用例的前置、主體和後置部分;最後調用hook函數pytest_runtest_logfinish來結束記錄日誌信息,並生成日誌報告。

 

```python
# src/_pytest/python.py

class Item(Node):
    def __init__(self, name, parent, config, session):
        # 初始化一些屬性和狀態信息
    
    def runtestprotocol(self):
        # 調用hook函數pytest_runtest_logstart表示開始記錄日誌信息
        self.ihook.pytest_runtest_logstart(nodeid=self.nodeid, location=self.location)
        # 調用runtest()方法來執行測試用例的前置、主體和後置部分,並返回一個列表reports
        reports = self.runtest()
        # 調用hook函數pytest_runtest_logfinish表示結束記錄日誌信息,並生成日誌報告
        self.ihook.pytest_runtest_logfinish(nodeid=self.nodeid, location=self.location)
        return reports
    
    def runtest(self):
        # 創建一個空列表reports
        reports = []
        # 調用_setup()方法來執行測試用例的前置操作,並將返回的報告添加到reports列表中
        reports.append(self._setup())
        # 如果前置操作沒有失敗或跳過,則調用_call()方法來執行測試用例的主體部分,並將返回的報告添加到reports列表中
        if reports[-1].passed:
            reports.append(self._call())
        # 調用_teardown()方法來執行測試用例的後置操作,並將返回的報告添加到reports列表中
        reports.append(self._teardown())
        # 返回reports列表
        return reports
    
    def _setup(self):
        # 調用hook函數pytest_runtest_setup表示開始執行前置操作,並返回一個報告setup_report
        setup_report = self.ihook.pytest_runtest_setup(item=self)
        return setup_report
    
    def _call(self):
        # 調用hook函數pytest_runtest_call表示開始執行主體部分,並返回一個報告call_report
        call_report = self.ihook.pytest_runtest_call(item=self)
        return call_report
    
    def _teardown(self):
        # 調用hook函數pytest_runtest_teardown表示開始執行後置操作,並返回一個報告teardown_report
        teardown_report = self.ihook.pytest_runtest_teardown(item=self)
        return teardown_report
```

 

總結:

Pytest的載入流程大致如下:

- Pytest首先會解析命令行參數,確定要執行的測試文件、測試目錄、測試類、測試函數等,以及一些配置選項。
- Pytest會根據配置文件(pytest.ini、setup.cfg、tox.ini等)和命令行參數,創建一個Config對象,用於存儲配置信息。
- Pytest會創建一個Session對象,用於管理整個測試過程的上下文信息,包括收集測試用例、執行測試用例、生成測試報告等。
- Pytest會調用hook函數pytest_sessionstart,表示測試會話開始。
- Pytest會調用hook函數pytest_collectstart,表示開始收集測試用例。
- Pytest會根據Config對象中的信息,遞歸遍歷指定的測試文件或目錄,尋找符合pytest約定的測試用例(以test_開頭的函數或方法,以Test開頭的類等)。
- Pytest會將找到的測試用例封裝成Item對象,並添加到Session對象的items列表中。Item對象包含了測試用例的名稱、位置、參數化信息、標記信息等。
- Pytest會調用hook函數pytest_collectreport,表示收集測試用例結束,並生成收集報告。
- Pytest會調用hook函數pytest_collection_modifyitems,允許對收集到的Item對象進行修改,例如重新排序、添加或刪除標記等。
- Pytest會調用hook函數pytest_deselected,表示從收集到的Item對象中篩選出需要執行的Item對象,並將不需要執行的Item對象放入Session對象的deselected列表中。
- Pytest會調用hook函數pytest_collection_finish,表示收集和篩選測試用例完成,並返回最終要執行的Item對象列表。
- Pytest會根據是否使用多進程或多線程模式,創建相應的WorkerController對象,用於管理多個Worker對象。Worker對象負責執行具體的測試用例,並將結果返回給WorkerController對象。
- Pytest會調用hook函數pytest_runtestloop,表示開始迴圈執行測試用例。
- Pytest會遍歷Session對象中的items列表,依次取出每個Item對象,並調用hook函數pytest_runtest_protocol,表示開始執行單個測試用例的協議。
- Pytest會調用hook函數pytest_runtest_logstart,表示開始記錄單個測試用例的日誌信息。
- Pytest會調用hook函數pytest_runtest_setup,表示開始執行單個測試用例的前置操作(例如setup函數或方法)。
- Pytest會調用hook函數pytest_runtest_call,表示開始執行單個測試用例的主體部分(例如測試函數或方法)。
- Pytest會調用hook函數pytest_runtest_teardown,表示開始執行單個測試用例的後置操作(例如teardown函數或方法)。
- Pytest會調用hook函數pytest_runtest_logfinish,表示結束記錄單個測試用例的日誌信息,並生成日誌報告。
- Pytest會調用hook函數pytest_runtest_makereport,表示根據單個測試用例的執行結果,生成測試報告(包括setup、call和teardown三個階段的報告)。
- Pytest會重覆上述步驟,直到所有的Item對象都被執行完畢。
- Pytest會調用hook函數pytest_sessionfinish,表示測試會話結束,並生成最終的測試報告(包括所有Item對象的報告)。
- Pytest會調用hook函數pytest_terminal_summary,表示在終端輸出最終的測試結果和統計信息。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # el-autocomplete核心參數 可以實現非同步的數據拉取,從非同步返回的數據中,選擇需要的結果,並回顯到文本框中。 ## fetch-suggestions 回調列表,非同步的方式獲取數據列表,顯示在列表框中 ## @select 當選中某一項時,會觸發這個方法,將數據獲取到,這時,我們可以將 ...
  • # 核心原理 長鏈接轉為短鏈接的核心原理是: 將短鏈接與原始長鏈接做一個映射,訪問短鏈接的時候,通過重定向的方式轉到長鏈接。 # 應用場景 比如分享功能,查看分享信息的原始鏈接通常是很長的,直接發給用戶,體驗不是很好,這時候就可以將其映射為一個短鏈接再發給用戶。 又比如我們熟知的百度網盤分享文件,雖 ...
  • ##一、定義 **講一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。建造者模式是一種創建型模式。** ##二、描述 **包含以下四個角色:** ![](https://img2023.cnblogs.com/blog/1780813/202305/1780813-202305 ...
  • 你想成為一名架構師,對嗎?別對我撒謊,我知道你想成為架構師。即使你不想,你還是想成為一名更好的開發者。否則,你就不會花時間閱讀這篇文章。 這種態度值得贊賞。畢竟,我們都希望在自己所從事的領域變得更好,即使不能稱為最好。我在這裡就是為了幫助你實現這一目標。 那麼,你如何成為一名架構師呢?當然是通過學習 ...
  • #### 本文為[李你幹嘛](https://www.cnblogs.com/liniganma)原創,轉載請註明出處:[Pybind11綁定C++抽象類(DLL介面)](https://www.cnblogs.com/liniganma/p/17666063.html) # 摘要 假設我們將DLL ...
  • 數據類型是編程中的重要概念。數據類型指定了變數值的大小和類型。 Go是靜態類型的,這意味著一旦變數類型被定義,它只能存儲該類型的數據。 Go有三種基本數據類型: - bool:表示布爾值,要麼是true,要麼是false。 - 數值型:表示整數類型、浮點數值和複數類型。 - string:表示字元串 ...
  • ### Java 8 的改進 - 速度更快 - 代碼更少(**Lambda表達式**) - 引入強大的**Stream API** - 便於並行 - 最大化減少空指針異常(**Optional**) - **Nashorn**引擎,允許在JVM上運行**js**應用 - **並行流**就是把一個內容 ...
  • 概述 Redis底層有六種數據類型包括:簡單動態字元串、雙向鏈表、壓縮列表、哈希表、跳錶和整數數組。這六種數據結構五大數據類型關係如下: String:簡單動態字元串 List:雙向鏈表、壓縮列表 Hash:壓縮列表、哈希表 Sorted Set:壓縮列表、跳錶 Set:哈希表、整數數組 數據類型和 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...