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

Python单元测试中如何Mock由函数生成的全局变量?

解决Python中Mock全局变量(导入时自动初始化)的问题

问题根源

你当前的测试失效,核心原因是模块导入时机和Mock时机不匹配:
当你执行from request_auth import *时,request_auth.py会被立即加载,其中config = get_config_secret()这行代码会同步执行——此时你的Mock装饰器还没生效,真实的get_config_secret()已经被调用(甚至可能因为缺少环境变量直接报错),后续的Mock无法改变已经初始化完成的config变量。

修复方案

方案1:调整Mock和模块导入的顺序

在测试中,先完成Mock配置,再导入目标模块,确保模块加载时Mock已经生效。修改后的测试代码如下:

import unittest
from unittest.mock import patch
import os


class TestRequestAuth(unittest.TestCase):

    @patch.dict(os.environ, {"SECRETS_NAME": "test-secret", "AWS_REGION": "us-east-1"})
    @patch("secrets.get_config_secret")
    def test_get_api_root_url(self, mock_get_config_secret):
        # 先设置Mock返回值
        mock_get_config_secret.return_value = {"url": "https://test.test.com/"}
        # 此时再导入目标函数/模块
        from request_auth import get_api_root_url
        self.assertEqual(get_api_root_url(), "https://test.test.com/")

这里额外加了@patch.dict(os.environ)是为了避免导入secrets.py时,因为找不到SECRETS_NAME和AWS_REGION环境变量抛出异常。

方案2:重构代码,延迟初始化全局变量

如果不想每次测试都调整导入顺序,可以修改request_auth.py的代码逻辑,把config的初始化从模块加载阶段延迟到第一次使用时:

# request_auth.py 修改部分
import requests
from secrets import get_config_secret
from datetime import datetime, timedelta

token = None

# 替换原来的直接赋值,改成延迟加载函数
def get_config():
    if not hasattr(get_config, "_cached_config"):
        get_config._cached_config = get_config_secret()
    return get_config._cached_config

def get_api_root_url():
    # 调用延迟加载函数获取配置
    return get_config()["url"]

# 其他用到config的地方也替换成get_config(),比如acquire_token:
def acquire_token():
    config = get_config()
    auth_payload = {
        "grant_type" : "client_credentials",
        "client_id" : config["id"]
    }
    response = requests.post(config["url"], data = auth_payload, verify = verifySSL())
    
    global token
    token= dict()
    token["authToken"] = response.json()["access_token"]
    token["expiry"] = datetime.now() + timedelta(minutes = 10)

修改后,模块加载时不会执行get_config_secret(),只有第一次调用get_config()时才会触发,测试时可以自由Mockget_config_secret(),无需调整导入顺序:

# 测试代码可以简化成
import unittest
from unittest.mock import patch
import os
from request_auth import get_api_root_url


class TestRequestAuth(unittest.TestCase):

    @patch.dict(os.environ, {"SECRETS_NAME": "test-secret", "AWS_REGION": "us-east-1"})
    @patch("secrets.get_config_secret")
    def test_get_api_root_url(self, mock_get_config_secret):
        mock_get_config_secret.return_value = {"url": "https://test.test.com/"}
        self.assertEqual(get_api_root_url(), "https://test.test.com/")

额外说明

  • 如果你想直接Mockrequest_auth里的config变量,必须确保在模块加载后立即patch,但这种方式不如Mock依赖函数灵活,因为其他函数(比如acquire_token)可能会直接读取原始的config值。
  • 测试acquire_token这类依赖网络请求的函数时,还需要额外Mockrequests.post,避免真实发送请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 11:54:51