Pytest系列(31) - config.cache 使用

基礎介紹

官方地址:https://docs.pytest.org/en/8.0.x/reference/reference.html#config-cache

  • pytest 中,cache 是一個非常有用的功能,它允許測試會話之間持久化狀態
  • 這意味着可以在一次測試運行中存儲一些值,並在後續的測試運行中訪問這些值

如何使用 cache

cache 對象通過 pytestFixtureRequest 對象提供,通常在一個 fixture 中獲取並返回它

@fixture
def cache(request: FixtureRequest) -> Cache:
    """Return a cache object that can persist state between testing sessions.

    cache.get(key, default)
    cache.set(key, value)

    Keys must be ``/`` separated strings, where the first part is usually the
    name of your plugin or application to avoid clashes with other cache users.

    Values can be any object handled by the json stdlib module.
    """
    assert request.config.cache is not None
    return request.config.cache

image.png

  • pytest 的緩存機制是通過一個名爲 .pytest_cache 的目錄實現的,該目錄位於項目的根目錄下
  • config.cache 對象提供了一個簡單的鍵值存儲接口,允許測試代碼讀取和寫入緩存數據
  • 一個 key 一個文件

存儲和檢索數據

cache 對象提供了兩個主要方法:getset

  • get(key, default=None) 方法用於檢索之前存儲的值。如果指定的 key 不存在,則返回 default 值。
  • set(key, value) 方法用於存儲值。key 應該是一個字符串,而 value 可以是任何可以被 json 標準庫模塊處理的對象。
# 設置緩存值
config.cache.set("key", "value")

# 獲取緩存值
value = config.cache.get("key", None)  # 如果鍵不存在,返回 None

鍵建議使用 / 分隔的字符串,其中第一部分通常是你的插件或應用程序的名稱,以避免與其他使用 cache 的代碼衝突

示例

假設有一個測試,需要從外部API獲取數據,但這個操作很耗時,可以在第一次運行測試時從API獲取數據,並將其存儲在 cache 中。在後續的測試運行中,可以直接從 cache 中檢索數據,避免重複的API調用

def test_external_api(cache):
    # 嘗試從緩存中獲取數據
    data = cache.get('external_api/data', default=None)
    if data is None:
        # 如果緩存中沒有數據,則從API獲取並存儲到緩存中
        data = fetch_data_from_external_api()  # 假設這是一個函數來獲取數據
        cache.set('external_api/data', data)
    
    # 使用數據進行測試
    assert data is not None

源碼解析

pytest 的緩存機制是在 _pytest/cacheprovider.py 文件中實現的

def get(self, key: str, default):
    """Return the cached value for the given key.

    If no value was yet cached or the value cannot be read, the specified
    default is returned.

    :param key:
        Must be a ``/`` separated value. Usually the first
        name is the name of your plugin or your application.
    :param default:
        The value to return in case of a cache-miss or invalid cache value.
    """
    path = self._getvaluepath(key)
    try:
        with path.open("r") as f:
            return json.load(f)
    except (ValueError, OSError):
        return default

def set(self, key: str, value: object) -> None:
    """Save value for the given key.

    :param key:
        Must be a ``/`` separated value. Usually the first
        name is the name of your plugin or your application.
    :param value:
        Must be of any combination of basic python types,
        including nested types like lists of dictionaries.
    """
    path = self._getvaluepath(key)
    try:
        if path.parent.is_dir():
            cache_dir_exists_already = True
        else:
            cache_dir_exists_already = self._cachedir.exists()
            path.parent.mkdir(exist_ok=True, parents=True)
    except OSError:
        self.warn("could not create cache path {path}", path=path, _ispytest=True)
        return
    if not cache_dir_exists_already:
        self._ensure_supporting_files()
    data = json.dumps(value, indent=2)
    try:
        f = path.open("w")
    except OSError:
        self.warn("cache could not write path {path}", path=path, _ispytest=True)
    else:
        with f:
            f.write(data)
  • 代碼還是比較簡單的,key 就是一個文件路徑,不存在則創建,然後寫入數據
  • 這些方法最終會將數據序列化爲 JSON 格式
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章