正确的模仿方法.smtplib.SMTP

问题描述

尝试模拟。在单元测试中修补对smtplib.SMTP.sendmail调用sendmail方法似乎已被成功模拟,我们可以将其查询Magicmock,但是sendmail模拟的calledcalled_args属性没有正确更新。似乎我没有正确应用补丁。

这是我正在尝试的简化示例:

import unittest.mock
with unittest.mock.patch('smtplib.SMTP',autospec=True) as mock:
    import smtplib
    smtp = smtplib.SMTP('localhost')
    smtp.sendmail('me','me','hello world\n')
    mock.assert_called()           # <--- this succeeds
    mock.sendmail.assert_called()  # <--- this fails

此示例生成

AssertionError: Expected 'sendmail' to have been called.

如果我将补丁更改为smtp.SMTP.sendmail;例如:

with unittest.mock.patch('smtplib.SMTP.sendmail.',autospec=True) as mock:
    ...

在这种情况下,我可以成功访问模拟的called_argscalled属性,但是由于允许进行smtplib.SMTP初始化,因此可以使用以下命令建立实际的smtp会话:主持人。这是单元测试,我希望没有实际的联网。

解决方法

我今天遇到了同样的问题,却忘记了我正在使用上下文,所以只需更改

mock.sendmail.assert_called()

mock.return_value.__enter__.return_value.sendmail.assert_called()

这看起来很混乱,但这是我的示例:

msg = EmailMessage()
msg['From'] = '[email protected]'
msg['To'] = '[email protected]'
msg['Subject'] = 'subject'
msg.set_content('content');

with patch('smtplib.SMTP',autospec=True) as mock_smtp:
    misc.send_email(msg)

    mock_smtp.assert_called()

    context = mock_smtp.return_value.__enter__.return_value
    context.ehlo.assert_called()
    context.starttls.assert_called()
    context.login.assert_called()
    context.send_message.assert_called_with(msg)
,

我将ustrymuugs的帖子标记为答案,但是我发现了另一种技术来对依赖于模拟method_calls的呼叫进行单元测试。

import unittest.mock
with unittest.mock.patch('smtplib.SMTP',autospec=True) as mock:
    import smtplib
    smtp = smtplib.SMTP('localhost')
    smtp.sendmail('me','you','hello world\n')

    # Validate sendmail() was called
    name,args,kwargs = smtpmock.method_calls.pop(0)
    self.assertEqual(name,'().sendmail')
    self.assertEqual({},kwargs)

    # Validate the sendmail() parameters
    from_,to_,body_ = args
    self.assertEqual('me',from_)
    self.assertEqual(['you'],to_)
    self.assertIn('hello world',body_)