4

我想模拟 SMTP 客户端表单的生成smtplib。以下代码:

from smtplib import SMTP
from unittest.mock import patch

with patch('smtplib.SMTP') as smtp:
    print(SMTP, smtp)

返回

<class 'smtplib.SMTP'> <MagicMock name='SMTP' id='140024329860320'>

暗示补丁失败。

编辑:有趣的是,这里描述的猴子补丁给出了相同的结果。

import smtplib
from smtplib import SMTP
from unittest.mock import MagicMock

smtp = MagicMock()
smtplib.SMTP = smtp
print(SMTP, smtp)
4

1 回答 1

2

我几乎不做任何补丁,但我相信你补丁要么太晚,要么是错误的事情。SMTP已经被导入,导致直接引用原始类——它将不再被查找smtplib。相反,您需要修补该引用。让我们用一个更现实的例子,在这个例子中你有module.pytest_module.py

module.py

import smtplib
from smtplib import SMTP # Basically a local variable

def get_smtp_unqualified():
    return SMTP # Doing a lookup in this module

def get_smtp_qualified():
    return smtplib.SMTP # Doing a lookup in smtplib

test_module.py

import unittest
from unittest import patch
from module import get_smtp_unqualified, get_smtp_qualified

class ModuleTest(unittest.TestCase):
    def test_get_smtp_unqualified(self):
        with patch('module.SMTP') as smtp:
            self.assertIs(smtp, get_smtp_unqualified())

    def test_get_smtp_qualified_local(self):
        with patch('module.smtplib.SMTP') as smtp:
            self.assertIs(smtp, get_smtp_qualified())

    def test_get_smtp_qualified_global(self):
        with patch('smtplib.SMTP') as smtp:
            self.assertIs(smtp, get_smtp_qualified())

只要您在查找之前及时修补,它就会做您想做的事——3 次通过测试。最早的时间是在导入除unittest. 那么这些模块还没有被导入smtplib.SMTP。更多关于这里。但是,当您的测试被拆分为多个模块时,它会变得很棘手。

修补本质上是肮脏的。你在搞乱别人的内部结构。为了让它发挥作用,你必须看看内部。如果内部发生变化,测试就会中断。这就是为什么您应该将其视为最后的手段并更喜欢不同的方式,例如依赖注入。那是一个完全不同的话题,但无论如何,不​​要依赖修补来防止消息流出——也要更改配置!

于 2017-12-17T20:07:24.420 回答