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

程序可运行但单元测试失败?全局变量作用域问题排查

问题分析与解决方案

问题描述

需要为一段依赖模块级全局变量的Python代码编写单元测试,原程序可正常运行,但测试执行时提示globTools is not defined。

待测试代码(简化版)

from services import myModule1
from services.spec1 import importedFunc
from services.spec2 import getTool
from services.spec3 import getDict

class myClass(object):
    def __init__(self, param1, param2):
        self.param1 = param1
        self.param2 = param2
        self.param3 = 0
        self.param4 = 0
    
    def myMethod(self):
        try:
            myVar1 = globalDict['key1']
            myVar2 = globalDict['key2']
            
            newVar = importedFunc(par1=myVar1, par2=myVar2, par3=extVar3)
            
            calcParam = myModule1.methodMod1(self.param1)
            
            self.param3 = calcParam["keyParam3"]
            self.param4 = newVar.meth1(self.param2)
            
            globTools.send_message(self.param3, self.param4)
            
        except:
            globTools.error_message(self.param3, self.param4)

        return


class myClass2(object):
    def __init__(self, *myclass2_params):
        # 初始化专属属性的代码
        self.add_objects()
    
    def add_objects(self):
        # 其他代码
        my_class = myClass(**necessary_params)
        # 其他代码
        return


if  __name__ == '__main__':
    globTools = getTool("my_program")
    globalDict = getDict(some_params)

    # 其他代码
    
    my_class2 = myClass2(**any_params)

    # 其他代码

测试代码(初始版本)

import unittest

from my_module import myClass

from services import myModule1
from services.spec1 import importedFunc
from services.spec2 import getTool
from services.spec3 import getDict

def test_myClass(unittest.TestCase):
    def setUp(self):
        globTools = getTool("my_program")
        globalDict = getDict(some_params)

    def test_myMethod(self):
        test_class = myClass(*necessary_parameters)
        test_res = test_class.myMethod()
        self.assertIsNotNone(test_res)

if __name__ == '__main__':
    unittest.main()

原因解析

  1. 作用域差异:原程序运行时,globTools、globalDict是在模块的全局作用域(if __name__ == '__main__'代码块)中定义的,myClass的方法与这些变量同属一个模块,因此可以直接访问。但测试运行时,my_module是作为被导入的模块存在的,测试代码中定义的变量属于测试模块的作用域,无法进入my_module的全局命名空间,导致myMethod找不到依赖的全局变量。
  2. 测试类写法错误:初始测试代码将测试用例定义为函数def test_myClass(unittest.TestCase),但unittest框架要求测试用例必须是继承自unittest.TestCase的类,这种写法会导致框架无法正确识别测试逻辑。

修复方案

方案一:手动注入模块级全局变量

直接在测试中把依赖的全局变量注入到my_module的模块命名空间中,模拟原程序运行时的环境:

import unittest
import my_module  # 直接导入整个模块,而非仅导入类
from services.spec2 import getTool
from services.spec3 import getDict

class TestMyClass(unittest.TestCase):
    def setUp(self):
        # 将全局变量注入到my_module的模块作用域
        my_module.globTools = getTool("my_program")
        my_module.globalDict = getDict(some_params)
        my_module.extVar3 = "test_ext_value"  # 补充myMethod依赖的extVar3变量

    def test_myMethod(self):
        test_class = my_module.myClass(param1="test_p1", param2="test_p2")
        test_res = test_class.myMethod()
        # 注意原myMethod的return是None,这里修正断言逻辑
        self.assertIsNone(test_res)

if __name__ == '__main__':
    unittest.main()

方案二:使用unittest.mock模拟依赖(推荐)

通过mock工具模拟所有外部依赖,避免依赖真实服务,同时能精准验证方法的调用逻辑:

import unittest
from unittest.mock import patch, MagicMock
from my_module import myClass

class TestMyClass(unittest.TestCase):
    def setUp(self):
        # 预定义所有mock对象的行为
        self.mock_globTools = MagicMock()
        self.mock_globalDict = {"key1": "test_key1", "key2": "test_key2"}
        self.mock_extVar3 = "test_ext_var"
        # 模拟importedFunc返回的对象及meth1方法的返回值
        self.mock_newVar = MagicMock()
        self.mock_newVar.meth1.return_value = 100
        # 模拟myModule1.methodMod1的返回值
        self.mock_calc_param = {"keyParam3": 200}

    @patch('my_module.globTools')
    @patch('my_module.globalDict')
    @patch('my_module.extVar3')
    @patch('my_module.importedFunc')
    @patch('my_module.myModule1.methodMod1')
    def test_myMethod(self, mock_method_mod1, mock_imported_func, mock_ext_var3, mock_global_dict, mock_glob_tools):
        # 为mock对象赋值
        mock_glob_tools = self.mock_globTools
        mock_global_dict = self.mock_globalDict
        mock_ext_var3 = self.mock_extVar3
        mock_imported_func.return_value = self.mock_newVar
        mock_method_mod1.return_value = self.mock_calc_param

        # 执行测试
        test_class = myClass(param1="test_p1", param2="test_p2")
        test_res = test_class.myMethod()

        # 验证方法调用是否符合预期
        mock_imported_func.assert_called_once_with(par1="test_key1", par2="test_key2", par3="test_ext_var")
        mock_method_mod1.assert_called_once_with("test_p1")
        self.mock_newVar.meth1.assert_called_once_with("test_p2")
        self.mock_globTools.send_message.assert_called_once_with(200, 100)
        self.assertIsNone(test_res)

if __name__ == '__main__':
    unittest.main()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 19:31:07