基礎介紹
官方地址:https://docs.pytest.org/en/8.0.x/reference/reference.html#config-cache
- 在
pytest
中,cache
是一個非常有用的功能,它允許測試會話之間持久化狀態 - 這意味着可以在一次測試運行中存儲一些值,並在後續的測試運行中訪問這些值
如何使用 cache
cache
對象通過 pytest
的 FixtureRequest
對象提供,通常在一個 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
- pytest 的緩存機制是通過一個名爲 .pytest_cache 的目錄實現的,該目錄位於項目的根目錄下
- config.cache 對象提供了一個簡單的鍵值存儲接口,允許測試代碼讀取和寫入緩存數據
- 一個 key 一個文件
存儲和檢索數據
cache
對象提供了兩個主要方法:get
和 set
。
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 格式