如何用pytest对pandas read_csv做Monkey Patch?补丁失效排查
问题分析与解决方案
一、Monkey Patch未生效的原因
你当前是对全局的pd.read_csv打补丁,但circle.py模块在导入pandas后,会持有自己的pd引用。测试时修改全局的pd不会影响circle模块内部已导入的pd对象,所以补丁没生效。
修复方法:
把补丁的目标改成circle模块内的pd.read_csv,也就是把测试代码里的:
monkeypatch.setattr(pd, "read_csv", mock_read_csv)
改成:
monkeypatch.setattr(circle.pd, "read_csv", mock_read_csv)
二、read_rads函数的逻辑错误
除了补丁问题,你的read_rads函数里用df.iterrows()的方式也错了:df.iterrows()返回的是(行索引, 行数据)的元组,你现在取x[0]拿到的是行索引(0、1、2...),不是CSV里的半径值。
修复方法:
修改read_rads里的列表推导,取行数据中的数值,比如用更简洁高效的方式:
return df.iloc[:, 0].apply(get_area).tolist()
三、测试数据注入方式的正确性
你用Monkey Patch替换read_csv返回固定DataFrame的方式是完全正确的,这是测试IO依赖代码的标准做法,能避免真实读取文件带来的不确定性。
修改后的完整代码
circle.py(修正read_rads)
import math import pandas as pd def get_area(r): if not type(r) in [int, float]: raise TypeError("Non numeric value") return math.pi*(r**2) def read_rads(): df = pd.read_csv('radii.csv', header=None, index_col=None) # 修正取值逻辑 return df.iloc[:, 0].apply(get_area).tolist() if __name__ == '__main__': print(get_area(2)) print(read_rads())
test_circle.py(修正补丁目标)
import pytest import circle import math import pandas as pd def test_get_area(): assert circle.get_area(1) == math.pi * 1**2 assert circle.get_area(3) == math.pi * 3**2 def test_if_get_area_for_string_input(): with pytest.raises(TypeError): circle.get_area('some string') def test_read_radii(monkeypatch): def mock_read_csv(*args, **kwargs): return pd.DataFrame([0,1,2,3,5]) # 修正补丁目标为circle模块内的pd.read_csv monkeypatch.setattr(circle.pd, "read_csv", mock_read_csv) res = circle.read_rads() assert res == [0.0, 3.141592653589793, 12.566370614359172, 28.274333882308138, 78.53981633974483]
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

