You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在无实际IO操作的前提下测试加载pickle文件的函数

无磁盘IO测试pickle加载函数的pytest实现方案

完全不需要生成test_list.pkl这类实体文件,核心思路是用mock模拟文件操作和pickle反序列化的返回值,直接在内存中注入测试用的字典列表,全程绕过真实磁盘IO。


先修正原函数的逻辑错误

你贴的原代码有两处明显笔误,直接运行会报错/逻辑不成立,先调整后再做测试:

  1. 列表推导式中错误地用外层loaded_list做键索引,应该取当前遍历到的entry的对应键
  2. 仅传入filter_key参数,没有指定需要匹配的目标值,判断相等时左右两边都是filter_key,过滤逻辑完全失效

修正后的函数代码:

import pickle

def load_pickled_list(path_to_file, filter_key=None, filter_value=None):
    with open(path_to_file, "rb") as file:
        loaded_list = pickle.load(file)

    if filter_key is not None and filter_value is not None:
        # 用get避免条目里不存在对应key时抛KeyError
        loaded_list = [entry for entry in loaded_list if entry.get(filter_key) == filter_value]

    return loaded_list

如果你原本的设计就是按key本身的值过滤,可以保留原来的参数逻辑,但是从常规使用场景看,加上filter_value才是可用的实现。


测试用例实现(mock构造测试替身)

可以直接用标准库自带的unittest.mock,或者搭配pytest-mock简化写法,全程不需要生成真实pkl文件。

依赖安装(可选)

如果用pytest-mock的话先安装:
pip install pytest pytest-mock

完整测试代码

import pytest
from unittest.mock import mock_open, patch
# 替换成你自己存放load_pickled_list的模块路径
from my_utils import load_pickled_list

# 直接在代码里定义两组测试用的字典列表,不需要序列化到磁盘
TEST_DATASET_1 = [
    {"id": 1, "type": "fruit", "name": "apple"},
    {"id": 2, "type": "vegetable", "name": "carrot"},
    {"id": 3, "type": "fruit", "name": "banana"}
]

TEST_DATASET_2 = [
    {"uid": 1001, "role": "admin", "active": True},
    {"uid": 1002, "role": "user", "active": False},
    {"uid": 1003, "role": "user", "active": True}
]

# 参数化传入多组测试场景
@pytest.mark.parametrize("mock_load_data,filter_kwargs,expected_result", [
    # 场景1:第一组数据,不过滤,返回全量
    (TEST_DATASET_1, {}, TEST_DATASET_1),
    # 场景2:第一组数据,过滤type为fruit的条目
    (TEST_DATASET_1, {"filter_key": "type", "filter_value": "fruit"}, [TEST_DATASET_1[0], TEST_DATASET_1[2]]),
    # 场景3:第二组数据,不过滤,返回全量
    (TEST_DATASET_2, {}, TEST_DATASET_2),
    # 场景4:第二组数据,过滤active为True的条目
    (TEST_DATASET_2, {"filter_key": "active", "filter_value": True}, [TEST_DATASET_2[0], TEST_DATASET_2[2]])
])
def test_load_pickled_list(mocker, mock_load_data, filter_kwargs, expected_result):
    # 给pickle.load打桩,直接返回我们准备好的测试数据,不需要真的反序列化
    mock_pickle_load = mocker.patch("pickle.load", return_value=mock_load_data)
    # 模拟open操作,不会真的读写磁盘
    mock_file_open = mocker.patch("builtins.open", mock_open(read_data=b""))

    # 传入任意假路径就行,因为open已经被替换成mock对象了
    result = load_pickled_list("fake_dir/mock_file.pkl", **filter_kwargs)

    # 校验结果符合预期
    assert result == expected_result
    # 校验函数确实按预期调用了文件读取和pickle加载
    mock_file_open.assert_called_once_with("fake_dir/mock_file.pkl", "rb")
    mock_pickle_load.assert_called_once()

不用pytest-mock的原生写法

如果不想装额外插件,用标准库的patch上下文管理器也能实现完全一样的效果:

def test_load_pickled_list_native():
    test_data = TEST_DATASET_1
    with patch("builtins.open", mock_open(read_data=b"")) as mock_file, \
         patch("pickle.load", return_value=test_data) as mock_load:
        res = load_pickled_list("any_fake_path.pkl")
        assert res == test_data
        mock_file.assert_called_once_with("any_fake_path.pkl", "rb")
        mock_load.assert_called_once()

注意:打桩的时候一定要patch被测函数所在命名空间下的open和pickle.load,不要patch错路径,不然mock不会生效。

这种写法的测试替身完全跳过了真实的pickle序列化/反序列化和磁盘IO,测试运行速度极快,也不会产生临时文件,所有测试数据都硬编码在用例里,可读性和可维护性都更高。

内容的提问来源于stack exchange,提问作者LuGeNat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 04:21:33