使用unittest.mock.patch打补丁smtplib.SMTP返回值不符合预期问题咨询
问题解答
1. 单元测试存在的问题
你写的测试有两处明显问题:
- 直接原因是你定义的
send函数本身没有任何return语句,不管内部sendmail返回什么值,send执行完默认返回None,这就是断言提示assert None == {}的根源。如果要拿到sendmail的返回值,需要修改send函数显式返回结果,示例修改如下:
def send(sender, to, subject="None", body="None", server="localhost"): message = email.message.Message() message['To'] = to message['From'] = sender message['Subject'] = subject message.set_payload(body) server = smtplib.SMTP(server) try: # 新增return返回sendmail的执行结果 return server.sendmail(sender, to, message.as_string()) finally: server.quit()
- 第二是补丁路径可能不符合mock规则。
unittest.mock.patch的核心规则是补丁要打在代码实际引用的位置,而非模块定义的位置。如果你的send函数定义在名为mail_utils的模块中,那么测试时应该补丁mail_utils.smtplib.SMTP,而非直接补丁smtplib.SMTP。因为send函数运行时是从自身所在模块的导入引用里找SMTP类,直接补丁源模块的SMTP不会影响到send函数的调用。
2. 打补丁失效的通用调试方法
- 查看mock调用统计:在测试调用完目标函数后,打印
mock.called、mock.call_count、mock.mock_calls这几个属性,就能直接判断补丁是否生效:如果mock.called为False,说明补丁位置错误,根本没有拦截到目标调用;如果mock_calls里存在预期外的调用记录,也能快速定位配置偏差。
调试示例:
def test_send(): with patch('smtplib.SMTP') as mock: instance = mock.return_value instance.sendmail.return_value = {} res = send('x@gmail.com', 'y@gmail.com', 'subject', 'body') # 新增调试打印 print("SMTP是否被调用:", mock.called) print("SMTP实例的所有调用记录:", instance.mock_calls) assert res == {}
- 对比对象ID:在
send函数中临时加一行打印id(smtplib.SMTP),在测试的patch块中也打印id(mock),如果两个ID不一致,说明补丁没有替换到目标位置,可直接判定是路径写错了。 - 用异常验证命中:给mock设置
side_effect=Exception("命中mock"),如果运行测试时没有抛出这个异常,就说明补丁完全没生效,优先排查路径问题。
内容的提问来源于stack exchange,提问作者sillypanda
相关产品推荐
相关产品推荐

