单元测试中如何模拟伪造配置路径?
单元测试中动态替换导入的静态路径变量
问题场景
原业务代码通过from custom_paths import CONFIG_PATH导入静态路径变量,用于加载配置文件:
from custom_paths import CONFIG_PATH import os import yaml class SomethingCool: def __init__(self, filepath: str) -> None: with open(os.path.join(CONFIG_PATH, filepath)) as fd: self.config = yaml.safe_load(fd) # 其他业务逻辑
需求:单元测试时需将CONFIG_PATH从默认的foobuzz/configs替换为tests/foobuzz/configs(由TESTS_PATH定义),但多次尝试后路径仍未改变。
无效的尝试
尝试1:monkeypatch 修改原模块变量
import os import custom_paths from my_module import SomethingCool TESTS_PATH = "tests/foobuzz" def return_fake_path(): return os.path.join(TESTS_PATH, "configs") def test_config_simple(monkeypatch): monkeypatch.setattr(custom_paths, "CONFIG_PATH", return_fake_path) coolstuff = SomethingCool("test.yml") # 断言逻辑
尝试2:mock.patch 修改原模块变量
import os from unittest import mock from my_module import SomethingCool TESTS_PATH = "tests/foobuzz" FAKE_CONFIG_PATH = os.path.join(TESTS_PATH, "configs") @mock.patch("custom_paths.CONFIG_PATH", FAKE_CONFIG_PATH) def test_config_simple(): coolstuff = SomethingCool("test.yml") # 断言逻辑
核心原因
from custom_paths import CONFIG_PATH会将custom_paths模块中的CONFIG_PATH值复制一份到当前业务模块(比如my_module.py)的命名空间。后续修改custom_paths模块里的CONFIG_PATH,不会影响已经导入到业务模块的变量引用。
正确解决方案
需要修改的是业务模块中已经导入的CONFIG_PATH,而非原custom_paths模块中的变量。
方案1:使用 pytest monkeypatch
import os from my_module import SomethingCool import pytest TESTS_PATH = "tests/foobuzz" FAKE_CONFIG_PATH = os.path.join(TESTS_PATH, "configs") def test_config_simple(monkeypatch): # 替换业务模块my_module中的CONFIG_PATH import my_module monkeypatch.setattr(my_module, "CONFIG_PATH", FAKE_CONFIG_PATH) coolstuff = SomethingCool("test.yml") assert coolstuff.config == expected_config
方案2:使用 unittest.mock.patch
import os from unittest import mock from my_module import SomethingCool TESTS_PATH = "tests/foobuzz" FAKE_CONFIG_PATH = os.path.join(TESTS_PATH, "configs") # 目标是业务模块my_module中的CONFIG_PATH,而非原custom_paths模块 @mock.patch("my_module.CONFIG_PATH", FAKE_CONFIG_PATH) def test_config_simple(): coolstuff = SomethingCool("test.yml") assert coolstuff.config == expected_config
验证方法
可在业务代码的__init__方法中临时添加打印语句,确认路径是否替换成功:
class SomethingCool: def __init__(self, filepath: str) -> None: print(f"当前CONFIG_PATH: {CONFIG_PATH}") # 测试时查看是否为测试路径 with open(os.path.join(CONFIG_PATH, filepath)) as fd: self.config = yaml.safe_load(fd)
内容的提问来源于stack exchange,提问作者Minh
相关产品推荐
相关产品推荐

