Python单元测试问题:if语句内导入的模块未定义
NameError: name 'other_module' is not defined的问题 我来帮你梳理下问题根源和解决办法——你遇到的这个坑其实是Python模块加载机制导致的,很常见但容易踩:
为什么会报错?
当你在测试文件里执行import my_code的瞬间,my_code.py的所有顶层代码会被一次性执行完毕。此时some_module.DO_IMPORT的原始值是False,所以那个条件导入import other_module根本没跑起来。
后来你用@patch把some_module.DO_IMPORT改成True,但模块已经加载完成了,Python不会再回头重新执行那些顶层代码,所以my_func里访问other_module时,这个模块根本没被导入,自然就报NameError了。
另外还要提一下你代码里的几个小bug,不然就算解决了导入问题也会报错:
- Python的布尔值是大写的
True/False,不是小写的true/false - 测试类要继承
unittest.TestCase才能用self.assertTrue这些断言 my_func定义时带了self参数,调用时得传个对象进去(比如测试类的self)- 你测试方法名写成了
def_test_my_func,少了空格,应该是def test_my_func(self, *_):
几种可行的解决方案
方案1:在patch生效后重新加载模块
我们可以先把已经加载的my_code从sys.modules里删掉,然后重新导入,这样模块会在DO_IMPORT=True的上下文里重新执行所有顶层代码,包括那个条件导入:
修改后的测试代码:
from unittest.mock import patch, Mock import sys import importlib import unittest import my_code class TestMyCode(unittest.TestCase): @patch('my_code.some_module', DO_IMPORT=True) def test_my_func(self, *_): # 移除已加载的模块 del sys.modules['my_code'] # 重新导入,此时DO_IMPORT是True,会执行import other_module import my_code # 调用函数并断言 ret = my_code.my_func(self) self.assertTrue(ret)
方案2:把导入移到函数内部(修改原代码)
如果允许改动my_code.py,可以把import other_module放到my_func里面,这样每次调用函数时都会检查条件并执行导入,彻底避开模块加载时机的问题:
修改后的my_code.py:
import some_module def my_func(self): print(some_module.DO_IMPORT) if some_module.DO_IMPORT: import other_module # 把导入移到函数内 print(other_module.OTHER_VAR) return True return False
对应的测试代码修正语法问题后就能正常跑:
from unittest.mock import patch, Mock import unittest import my_code class TestMyCode(unittest.TestCase): @patch('my_code.other_module', OTHER_VAR='Other Var') @patch('my_code.some_module', DO_IMPORT=True) def test_my_func(self, *_): ret = my_code.my_func(self) self.assertTrue(ret)
方案3:在导入my_code前先patch
另一种思路是,确保import my_code发生在patch生效之后,这样模块加载时DO_IMPORT已经是True,条件导入会直接执行:
修改后的测试代码:
from unittest.mock import patch, Mock import unittest class TestMyCode(unittest.TestCase): @patch('my_code.some_module', DO_IMPORT=True) @patch('my_code.other_module', OTHER_VAR='Other Var') def test_my_func(self, *_): # 把import放到patch上下文里,确保导入时DO_IMPORT已经是True import my_code ret = my_code.my_func(self) self.assertTrue(ret)
这三种方案都能解决你的问题,选哪种看你是否愿意修改原代码,以及测试场景的需求。
内容的提问来源于stack exchange,提问作者Shubham Vadhera

