【pytest官方檔案】解讀- 如何自定義mark標記,並將測試用例的資料傳遞給fixture函數

2022-09-06 12:00:42

在之前的分享中,我們知道可以使用yield或者return關鍵字把fixture函數裡的值傳遞給test函數

這種方法很實用,比如我在fixture函數裡向資料庫裡插入必要的測試資料,那我就可以把相關資料返回給test函數用來做相關斷言查詢等操作。

那如果我想把test函數(也就是測試用例)中的資料傳給fixture函數使用,要如何實現呢?

直接先貼上一段範例程式碼:

import pytest


@pytest.fixture
def fixt(request):
    marker = request.node.get_closest_marker("fixt_data")
    if marker is None:
        # Handle missing marker in some way...
        data = None
    else:
        data = marker.args[0]

    # Do something with the data
    return data


@pytest.mark.fixt_data(42)
def test_fixt(fixt):
    assert fixt == 42

一、前置知識

程式碼中可能有2個知識點,可能有的小夥伴並不熟悉,分別來看下。

1. Mark 標記

什麼是mark標記,幹什麼用?

標記可以將後設資料應用於測試函數(注意,不能是fixture函數),後續可以通過fixture函數或者plugins外掛進行存取。

框架有一些內建的marks,也可以支援我們自定義

內建的在之前的系列分享中有出現過幾個,比如:

  • pytest.mark.parametrize:引數化
  • pytest.mark.skip:跳過測試用例
  • pytest.mark.skipif: 根據條件跳過用例

其他就不展開了,上述提到的分享文章連結會附在文末。

而在上述範例程式碼中,pytest.mark.fixt_data則是屬於自定義的mark標記,fixt_data我也可以改成fixt_pingguo也是可以的。

2. request

request本身也是一個fixture函數,但是很特殊,用於提供當前正在執行請求的上下文資訊。

在上述範例程式碼中,測試函數test_fixt請求了fixture函數fixt,那麼在這次請求中相關聯到的資訊就可以在request中獲得。
比如:

  • fixturename: 當前這個fixture函數的名稱
  • module: 當前測試函數所在的模組
  • scope:當前fixture函數作用範圍
  • node:基於當前測試範圍蒐集到的底層節點物件,這裡又包含了很多資訊。
    ...

就不一一展開了,有興趣的童鞋可以在編輯器裡打個斷點,檢視對應的資訊詳情。

二、通過自定義mark傳遞資料

回到範例程式碼,我們可以先直接執行一下程式碼。

測試是通過的(warning先忽略,因為沒有註冊自定義的mark),也就是說@pytest.mark.fixt_data(42)中的42是成功的帶到了fixture函數中,經過函數中的處理後最後返回出來。

其中的get_closest_marker("fixt_data")方法,是返回與名稱fixt_data匹配的第一個mark,從最近的級別到更遠的級別,比如從函數到模組級別。

所以在這裡,被找到的mark就是我們自定義的這個@pytest.mark.fixt_data(42)標記了。

1. 自定義mark知識點

標記是使用工廠物件pytest.mark動態建立的,用於裝飾器,所以我們可以用語法糖@直接使用即可。

mark物件被建立之後,就被會收集起來,然後可以通過fixture或帶有Node.iter_markers的勾點函數存取,可以存取到這個mark物件的屬性。

有 2 個屬性:

  • mark.args:這是個元組
  • mark.kwargs:這是個字典

所以我們可以使用上面的方式來進行傳參,比如現在新建一個自定義mark:

@pytest.mark.timeout(10, "slow", method="thread")
def test_function():
    ...

這裡傳參實際上就是

mark.args == (10, "slow")
mark.kwargs == {"method": "thread"}

回到最上方的範例程式碼,在fixture函數中就可以使用data = marker.args[0]來獲取到引數42

如果在測試函數上同時使用了多個自定義mark,那麼舉例測試函數最近的mark就會被首先迭代。比如:

@pytest.mark.timeout(10, "slow", method="thread")
@pytest.mark.slow
def test_function():
    ...

結果就是先@pytest.mark.slow,然後是@pytest.mark.timeout

2. 註冊自定義的mark

在執行最上方的範例程式碼時出現了一個warning,因為我們沒有註冊自定義的標記導致,現在來進行註冊。

新建pytest.ini組態檔,在裡面新增即可:

[pytest]
markers =
    fixt_data: pingguo test
    fixt_data2

這裡冒號:後面的描述是可選的,比如fixt_data2就是沒有新增描述。

重新執行下最上方的程式碼:

platform win32 -- Python 3.9.12, pytest-7.1.3, pluggy-1.0.0
rootdir: D:\PythonCode\my_python, configfile: pytest.ini
collected 1 item

usemarks.py .

============================== 1 passed in 0.00s ==============================

Process finished with exit code 0

註冊完成。

pytest合集見連線