從pytest原始碼的角度分析pytest工作原理

清风软件测试开发發表於2024-07-30

從pytest原始碼的角度分析pytest工作原理

pytest 原始碼的角度來分析其工作原理,我們需要關注幾個關鍵的部分,特別是 pytest 的啟動過程以及測試的收集與執行。下面是基於 pytest 原始碼的一個高層次的概述。

pytest 的啟動過程

  1. 命令列解析:

    • pytest 的入口點是 conftest.py 檔案中的 pytest.main() 函式。
    • 在這個函式中,首先透過 pytest.config.get_config() 獲取配置。
    • 接著使用 pytest.config.parse() 來解析命令列引數。
  2. 配置載入:

    • pytest 會在當前目錄及其父目錄遞迴地查詢配置檔案,比如 pytest.inipyproject.toml
    • 使用 pytest.config.Config 類來儲存配置資訊。
  3. 外掛管理:

    • 透過 pytest.hookspecpytest.pluginmanager 來管理外掛。
    • 外掛可以在各個階段被註冊並呼叫。

測試收集過程

  1. 收集器初始化:

    • pytest 使用 pytest.collect 模組來處理測試收集。
    • Session.from_parent 方法建立一個新的 Session 例項。
    • Collector.from_parent 方法用於構建收集器樹。
  2. 測試檔案發現:

    • pytest 透過 Session.perform_collect 方法來遍歷目錄結構並發現測試模組。
    • File.from_parent 方法用於建立 File 例項來代表測試檔案。
    • Function.from_parent 方法用於建立 Function 例項來代表測試函式。
  3. 測試項構建:

    • 一旦發現了測試檔案,就會透過 collect 方法來收集檔案中的測試函式。
    • 測試函式會被轉換成 Item 例項。

測試執行過程

  1. 測試項準備:

    • 在測試開始之前,會呼叫 Session.perform_setup 方法來進行一些預處理。
    • 這個階段可能包括設定環境變數、初始化資料庫連線等。
  2. 測試項執行:

    • Session.runtestloop 方法控制測試項的實際執行。
    • 對於每一個 Item 例項,都會呼叫 Session.perform_test 方法來執行測試。
  3. 測試結果收集:

    • 測試執行的結果會被收集並儲存在 Item 例項中。
    • 可能會觸發 pytest_runtest_logreport hook,該 hook 被用來處理測試報告。
  4. 異常處理:

    • 如果測試過程中發生異常,pytest 會捕獲這些異常並記錄下來。
    • 異常可以透過 pytest_runtest_makereport hook 來處理。

測試報告生成

  1. Session 例項負責收集所有的測試結果。
  2. Session.exitstatus 屬性會根據測試結果來確定程式的退出狀態碼。
  3. pytest 可以生成多種格式的報告,這取決於安裝的外掛。

示例程式碼片段

下面是一些示例程式碼片段,展示了 pytest 原始碼中的關鍵部分:

# pytest/conftest.py
def main(args=None):
    # 解析命令列引數
    config = get_config(args)
    # 載入外掛
    pm = PluginManager()
    pm.load_setuptools_entrypoints('pytest11')
    # 建立 Session 例項
    session = Session.from_parent(config, plugins=pm)
    # 執行測試
    session.runtestloop()
    # 返回退出狀態
    return session.exitstatus

# pytest/collect.py
def perform_collect(session, collector):
    # 收集測試檔案和測試函式
    items = []
    for item in collector.collect():
        items.append(item)
    return items

# pytest/runner.py
def runtest_protocol(item, nextitem):
    # 執行測試項
    report = item.runtest()
    if report is None:
        # 處理異常情況
        report = item.makereport()
    # 處理測試報告
    item.session._hookmanager.hook.pytest_runtest_logreport(report=report)

相關文章