Trying to mock.patch a call to smtplib.SMTP.sendmail
in a unittest. The sendmail
method appears to be successfully mocked and we can query it as MagicMock
, but the called
and called_args
attributes of the sendmail mock are not correctly updated. It seems likely I'm not applying the patch correctly.
Here's a simplified example of what I'm trying:
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
This example generates:
AssertionError: Expected 'sendmail' to have been called.
If I alter the patch to smtp.SMTP.sendmail
; eg:
with unittest.mock.patch('smtplib.SMTP.sendmail.', autospec=True) as mock:
...
I can successfully access the called_args
and called
attributes of the mock in this case, but because the smtplib.SMTP
initialization was allowed to take place, an actual smtp-session is established with a host. This is unittesting, and I'd prefer no actual networking take place.
I had the same issue today and forgot that I'm using a context, so just change
mock.sendmail.assert_called()
to
mock.return_value.__enter__.return_value.sendmail.assert_called()
That looks messy but here's my example:
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)
I marked dustymugs's post as the answer, but I discovered another technique to unittest the call that depends on the mocks 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_)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With