Python's assert_called_with, is there a wildcard character?
Question:
Suppose I have a class in python set up like this.
from somewhere import sendmail
class MyClass:
def __init__(self, **kargs):
self.sendmail = kwargs.get("sendmail", sendmail) #if we can't find it, use imported def
def publish():
#lots of irrelevant code
#and then
self.sendmail(mail_to, mail_from, subject, body, format= 'html')
So as you can see, I have sort of given myself the option to parameterize which function I use for self.sendmail
Now in the test file.
Class Tester():
kwargs = {"sendmail": MagicMock(mail_from= None, mail_to= None, subject= None, body= None, format= None)}
self.myclass = MyClass(**kwargs)
##later on
def testDefaultEmailHeader():
default_subject = "Hello World"
self.myclass.publish()
self.myclass.sendmail.assert_called() #this is doing just fine
self.myclass.sendmail.assert_called_with(default_subject) #this is having issues
For some reason I am getting the error message
AssertionError: Expected call: mock('Hello World')
Actual Call : mock('defaultmt', 'defaultmf', 'Hello World', 'default_body', format= 'html')
So basically, the assert is expecting sendmail to be called with only one variable, when it ends up being called with all 5. The thing is, I don’t care about what the other 4 variables are! I just want to make sure it is called with the correct subject.
I tried the mock place holder ANY, and got the same thing
self.myclass.sendmail.assert_called_with(ANY, ANY, 'Hello World', ANY, ANY)
AssertionError: Expected call: mock(<ANY>, <ANY>, 'Hello World', <ANY>, <ANY>)
Actual Call : mock('defaultmt', 'defaultmf', 'Hello World', 'default_body, 'format= 'html')
Really unsure on how to proceed with this one. Anyone have any advice if we only care about one of the variable and want to ignore the rest?
Answers:
If you’re calling sendmail
with a named parameter subject
then it’s better to check whether the named argument matches what you expect:
args, kwargs = self.myclass.sendmail.call_args
self.assertEqual(kwargs['subject'], "Hello World")
This does assume both implementations of sendmail
have a named parameter called subject
. If that’s not the case you can do the same with a positional parameter:
args, kwargs = self.myclass.sendmail.call_args
self.assertTrue("Hello World" in args)
You can be explicit about the position of the argument (i.e., the first argument or the third argument that’s passed to sendmail
but that depends on the implementation of sendmail
being tested).
Using ANY
from unittest.mock
, wild cards are possible with assert_called_with
:
from unittest.mock import ANY
self.myclass.sendmail.assert_called_with(
subject="Hello World",
mail_from=ANY,
mail_to=ANY,
body=ANY,
format=ANY,
)
args, _ = your_mock.call_args
assert "must-exist-string" in str(args)
Suppose I have a class in python set up like this.
from somewhere import sendmail
class MyClass:
def __init__(self, **kargs):
self.sendmail = kwargs.get("sendmail", sendmail) #if we can't find it, use imported def
def publish():
#lots of irrelevant code
#and then
self.sendmail(mail_to, mail_from, subject, body, format= 'html')
So as you can see, I have sort of given myself the option to parameterize which function I use for self.sendmail
Now in the test file.
Class Tester():
kwargs = {"sendmail": MagicMock(mail_from= None, mail_to= None, subject= None, body= None, format= None)}
self.myclass = MyClass(**kwargs)
##later on
def testDefaultEmailHeader():
default_subject = "Hello World"
self.myclass.publish()
self.myclass.sendmail.assert_called() #this is doing just fine
self.myclass.sendmail.assert_called_with(default_subject) #this is having issues
For some reason I am getting the error message
AssertionError: Expected call: mock('Hello World')
Actual Call : mock('defaultmt', 'defaultmf', 'Hello World', 'default_body', format= 'html')
So basically, the assert is expecting sendmail to be called with only one variable, when it ends up being called with all 5. The thing is, I don’t care about what the other 4 variables are! I just want to make sure it is called with the correct subject.
I tried the mock place holder ANY, and got the same thing
self.myclass.sendmail.assert_called_with(ANY, ANY, 'Hello World', ANY, ANY)
AssertionError: Expected call: mock(<ANY>, <ANY>, 'Hello World', <ANY>, <ANY>)
Actual Call : mock('defaultmt', 'defaultmf', 'Hello World', 'default_body, 'format= 'html')
Really unsure on how to proceed with this one. Anyone have any advice if we only care about one of the variable and want to ignore the rest?
If you’re calling sendmail
with a named parameter subject
then it’s better to check whether the named argument matches what you expect:
args, kwargs = self.myclass.sendmail.call_args
self.assertEqual(kwargs['subject'], "Hello World")
This does assume both implementations of sendmail
have a named parameter called subject
. If that’s not the case you can do the same with a positional parameter:
args, kwargs = self.myclass.sendmail.call_args
self.assertTrue("Hello World" in args)
You can be explicit about the position of the argument (i.e., the first argument or the third argument that’s passed to sendmail
but that depends on the implementation of sendmail
being tested).
Using ANY
from unittest.mock
, wild cards are possible with assert_called_with
:
from unittest.mock import ANY
self.myclass.sendmail.assert_called_with(
subject="Hello World",
mail_from=ANY,
mail_to=ANY,
body=ANY,
format=ANY,
)
args, _ = your_mock.call_args
assert "must-exist-string" in str(args)