使用unittest和mock测试全局变量列表更新时断言失败问题
问题分析与修复方案
先贴出你提供的代码:
file.py
GLOB_VAR = [] def my_func(x, msg): if x: logging.warning(msg) GLOB_VAR.append(x)
test.py(原代码)
@patch('file.GLOB_VAR', []) @patch('logging.Logger.warning') def test_file_with_msg(self, logging): x = 'test_x' msg = 'test_msg' my_func(x=x, msg=msg) logging.assert_called_with(message) assert x in GLOB_VAR
问题根源
- 笔误导致的前置错误:
logging.assert_called_with(message)里的message未定义,实际应该用你定义的msg,这行代码会先抛出NameError,根本走不到断言步骤。 - 全局变量引用不匹配:
@patch('file.GLOB_VAR', [])是把file模块中的GLOB_VAR替换成了一个全新的空列表,但如果你的测试代码是通过from file import GLOB_VAR导入的变量,这个导入的GLOB_VAR指向的是原全局列表,而my_func操作的是被patch后的新列表,所以断言x in GLOB_VAR必然失败。
修复方案
根据你“需要保留全局变量”的需求,分两种场景处理:
场景1:测试时临时替换全局变量,不影响原变量状态
用patch的new_callable参数创建临时列表,同时在测试中通过模块引用被patch后的变量:
from unittest.mock import patch import file from file import my_func import logging class TestMyFunc: # 用new_callable创建新的空列表,不会修改原全局变量 @patch('file.GLOB_VAR', new_callable=list) @patch('logging.Logger.warning') def test_file_with_msg(self, mock_warning, mock_glob_var): x = 'test_x' msg = 'test_msg' my_func(x=x, msg=msg) # 修正笔误,用正确的变量名msg mock_warning.assert_called_with(msg) # 通过file模块访问被patch后的全局变量 assert x in file.GLOB_VAR # 或者直接用mock_glob_var参数访问 assert x in mock_glob_var
场景2:测试后保留原全局变量的修改(即测试后GLOB_VAR确实包含x)
直接去掉@patch('file.GLOB_VAR', []),手动控制全局变量的状态:
from unittest.mock import patch import file from file import my_func import logging class TestMyFunc: @patch('logging.Logger.warning') def test_file_with_msg(self, mock_warning): x = 'test_x' msg = 'test_msg' # 测试前备份原状态(可选,根据是否需要恢复) original_data = file.GLOB_VAR.copy() file.GLOB_VAR.clear() my_func(x=x, msg=msg) mock_warning.assert_called_with(msg) # 直接断言模块中的全局变量 assert x in file.GLOB_VAR # 测试后恢复原状态(如果不需要保留测试数据,就加上这段) file.GLOB_VAR.clear() file.GLOB_VAR.extend(original_data)
关键说明
- 永远通过模块(比如
file.GLOB_VAR)访问全局变量,而不是直接导入后使用,避免引用不匹配的问题。 - 如果只是测试逻辑不需要保留原全局变量,用
new_callable的方式更安全,不会污染原变量状态。
内容的提问来源于stack exchange,提问作者Potis23
相关产品推荐
相关产品推荐

