如何在Pytest中Mock直接导入的read_sql函数(无需重构代码)
问题描述
我正在用Pytest为Python项目编写单元测试,需要Mock外部仓库模块sql_services中的read_sql函数。目前只能通过import sql_services后调用sql_services.read_sql的方式Mock该函数,但无法Mock通过from sql_services import read_sql直接导入的函数,而我的代码库正采用这种直接导入的方式。
待测试的entry.py代码:
from sql_services import read_sql def foo(): df = read_sql("SELECT * FROM schema.table") return df
我的单元测试文件代码:
import pytest import unittest.mock as mock import pandas as pd import sql_services from entry import foo @mock.patch("sql_services.read_sql") def test_read_sql(mock_read_sql): mock_read_sql.return_value = pd.DataFrame() df = sql_services.read_sql("SELECT * FROM schema.table") assert df.empty @mock.patch("sql_services.read_sql") def test_do_a_read(mock_read_sql): mock_read_sql.return_value = pd.DataFrame() df = foo() assert df.empty
第一个测试通过,但第二个测试失败,因为foo实际从数据库读取了数据。请问是否有无需重构整个代码库即可Mockfoo中该函数的方法?
解决方案
问题核心是Mock路径不正确:当使用from sql_services import read_sql时,entry模块会生成一个本地的read_sql引用,指向原模块的函数。此时Mock原模块sql_services.read_sql,并不会改变entry模块里已经导入的那个本地引用。
无需重构代码的解决方法是:Mockentry模块中本地的read_sql引用,也就是把测试中的@mock.patch("sql_services.read_sql")替换为@mock.patch("entry.read_sql")。
修改后的测试代码:
import pytest import unittest.mock as mock import pandas as pd from entry import foo @mock.patch("entry.read_sql") def test_do_a_read(mock_read_sql): mock_read_sql.return_value = pd.DataFrame() df = foo() assert df.empty
这样修改后,测试会替换entry模块中read_sql的本地引用,foo调用的就是Mock后的函数,不会再访问真实数据库。
内容的提问来源于stack exchange,提问作者Jacob Gladman
相关产品推荐
相关产品推荐

