如何拦截所有pandas DataFrame的to_csv方法,不受初始化方式影响?
问题:拦截pandas所有DataFrame的to_csv调用以避免测试时写入数据
我正在测试一段使用pandas处理数据的代码,希望测试过程中避免写入数据。假设module.py中的代码如下:
import pandas as pd import dask.dataframe as dd def do_stuff() -> None: df = pd.DataFrame({'a': [1, 2, 3], 'b': [1, 2, 3]}) another_df = df.pivot_table(values='a', index='b') yet_another_df = another_df.groupby('b').sum() another_df.to_csv('data.csv') yet_another_df.to_csv('more_data.csv')
我尝试用pytest编写测试代码拦截to_csv调用,但自定义子类的方法无效——仅对直接创建的df生效,pivot_table、groupby等方法返回的仍是原生pandas DataFrame,依然会执行写入:
import module class NonWritingDataFrame(pd.DataFrame): def to_csv(self, *args, **kwargs): pass def test_do_stuff_returns_nothing(monkeypatch): monkeypatch.setattr(module, 'pd.DataFrame', NonWritingDataFrame) actual = module.do_stuff() assert actual is None
请问是否有简洁的方法可以替换所有pandas DataFrame的to_csv调用,无论DataFrame是通过何种方式创建的?
解决方案
方法1:直接猴子补丁替换原生DataFrame的to_csv
最简洁的方式是直接修改pd.DataFrame类的to_csv方法,让所有DataFrame实例(不管是怎么创建的)都使用空实现:
import pandas as pd import module def test_do_stuff_no_write(monkeypatch): # 替换to_csv为无操作函数 def mock_to_csv(*args, **kwargs): pass monkeypatch.setattr(pd.DataFrame, 'to_csv', mock_to_csv) actual = module.do_stuff() assert actual is None
原理:所有pandas DataFrame实例都会继承类的to_csv方法,直接替换类方法后,不管是通过pd.DataFrame()创建,还是通过pivot_table、groupby等方法生成的实例,都会使用这个被替换后的方法,彻底阻止写入。
方法2:使用unittest.mock验证调用(更灵活)
如果需要验证to_csv是否被正确调用(比如调用次数、传入的文件名是否正确),可以用unittest.mock.patch:
from unittest.mock import patch import module def test_do_stuff_to_csv_called(): with patch('pandas.DataFrame.to_csv') as mock_to_csv: module.do_stuff() # 验证to_csv被调用了2次 assert mock_to_csv.call_count == 2 # 验证第一次调用的文件名是'data.csv' mock_to_csv.assert_any_call('data.csv') # 验证第二次调用的文件名是'more_data.csv' mock_to_csv.assert_any_call('more_data.csv')
原理:patch会临时替换pd.DataFrame.to_csv为Mock对象,既阻止了实际写入,还能方便地做断言验证,适合需要确认业务逻辑是否正确触发写入操作的场景。
注意事项
如果代码中用到了Dask DataFrame的to_csv,需要用同样的方式替换dd.DataFrame.to_csv,确保所有写入操作都被拦截。
内容的提问来源于stack exchange,提问作者houseofleft
相关产品推荐
相关产品推荐

