Python mock - Check if methods are called in mocked object - python

I have a certain piece of code that looks like this:
# file1.py
from module import Object
def method():
o = Object("param1")
o.do_something("param2")
I have unittests that look like this:
#patch("file1.Object")
class TestFile(unittest.TestCase):
def test_call(self, obj):
...
I can do obj.assert_called_with() in the unittest to verify that the constructor was called with certain parameters. Is it possible to verify that obj.do_something was called with certain parameters? My instinct is no, as the Mock is fully encapsulated within Object, but I was hoping there might be some other way.

You can do this, because the arguments are passed to the mock object.
This should work:
#patch("file1.Object")
class TestFile:
def test_call(self, obj):
method()
obj.assert_called_once_with("param1")
obj.return_value.do_something.assert_called_once_with("param2")
obj.return_value is the Object instance (which is a MagickMock object with the Object spec), and do_something is another mock in that object that is called with the given parameter.
As long as you are just passing arguments to mock objects, the mock will record this and you can check it. What you don't have is any side effects from the real function calls - so if the original do_something would call another function, this cannot be checked.

When you mock an object, it will mock the methods inside the object aswell. Therefore you are able to see if obj.do_something has been called with certain parameters as obj.do_something.assert_called_with()
For more information regarding unittest mocking can be found at the python library wiki https://docs.python.org/3/library/unittest.mock.html
A perfect example of what you are asking exist within that wiki source:
>>> mock = Mock()
>>> mock.method(1, 2, 3, test='wow')
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called_with(1, 2, 3, test='wow')
https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.assert_called_with
Regards, I see that you placed the the patching on the object, try placing it on the function instead, like:
class TestFile(unittest.TestCase):
#patch("file1.Object")
def test_call(self, obj):
...

Related

Mocking instantiated object vs. class in Python

Could someone help me understand the difference between unitest.patch instantianed objects and classes? I'm trying to mock calls to sagemaker.Session.describe_training_job.
I have a function that calls my_session = sagemaker.Session() then calls my_session.describe_training_job() later:
#my_module.py
import sagemaker
def do_something_with_session(local_session, job):
return local_session.describe_training_job(job)
def my_function(args):
my_session = sagemaker.Session()
description = do_something_with_session(my_session, args.job)
If I use the test below, the my_session object is a MagicMock as expected, but does not have the describe_training_job method:
#patch("sagemaker.Session")
def test_class(self, sagemaker_session):
sagemaker_session.describe_training_job = MagicMock(return_value=self.mock_return_value)
my_module.my_function(args=self.mock_args)
If I instead do this:
#patch("sagemaker.Session")
def test_class(self, sagemaker_session):
# Need to instantiate an object for this to work
session_object = sagemaker_session()
session_object.describe_training_job = MagicMock(return_value=self.mock_return_value)
my_module.my_function(self.mock_args)
Then the test works as expected: the my_session object is a MagicMock with a describe_training_job method that always returns the value I set it to.
Could someone help understand what the behavior is here? What I've noticed when is that the name parameter of the MagicMock is Session when I try to use the class, but Session() when I do instantiate the object. Not sure how that affects binding.
A mock is a placeholder object, it does not have the mocked methods (though it may know which methods can be called if using autospec). Every method call on the mock that you do not explicitly provide just returns another mock object.
If you mock a class, and that class is instantiated in the tested code, the resulting instance will be another mock. The instantation is just a __call__ for the mock, which returns another mock as every call does (and it always returns the same mock for the same call). Similar to the result of a function call, this mock can be accessed via return_value.
If you set an attribute on the class mock like in your first example, it is only bound to that class mock, not to the instance mock, so this allows to mock only class methods. The main point to understand here is that the class mock and the instance mock are not related like the class and the instance they are mocking. Both have the same Mock type, and the instance mock doesn't know the class mock that has created it.
This means that instance method attributes have always to be set on the instance mock, as you have done in your second example. Using return_value is eqivalent to that, so you could also write:
sagemaker_session.return_value.describe_training_job = MagicMock(return_value=self.mock_return_value)

How do I mock a class instance and assign attributes to that instance using mocker.patch.object() and pytest-mock?

I am trying to unit test a module (using pytest and pytest-mock) and I would like to create a mocked instance of a class with certain values for its attributes to be used in a #pytest.fixture
I think I've finally found what I'm looking for in patch.object and autospeccing but I don't understand what the arguments are supposed to be, specifically 'attribute'
patch.object(target, attribute, new=DEFAULT, spec=None, create=False,
spec_set=None, autospec=None, new_callable=None, **kwargs)
patch the named member (attribute) on an object (target) with a mock object.
I tried searching for patch.object() examples but I'm only seeing use cases related to mocking the return values for methods in a class.
Like in this example, a method is passed as the 'attribute' argument to patch.object().
Any help or hints in the right direction would be greatly appreciated!
An example would be the method that you're trying to test.
So, if I wanted to check to see if MyClass.method_a was called, I would do:
from .functions import MyClass # import MyClass from wherever
# it is used in the module.
with patch.object(MyClass, 'method_a') as mocked_myclass:
function_that_called_myclass_method_a()
mocked_myclass.assert_called()

How to determine if method is called using Python mock but does not replace the function body?

There are plenty of examples that shows how to assert a method has been called using Mock, eg. assert_called_with(), but all of them involve replacing the method with Mock instance.
What I want is a little bit different, I want the function to be executed normally without its body replaced, but still want to assert if the function has been called.
eg.
def dosomething(...)
# creates records that I will test later on.
....
def execute():
....
dosomething()
in my tests
def test_a(...):
with patch(dosomething...) as mocked:
execute()
mocked.assert_called_with()
I know I can test against the records that dosomething() creates instead. Yes I agree, but I just want to find out if it's possible to do per as my question.
Use Mock's wraps kwarg and pass it the original method.
For example,
>>> from unittest import mock
>>> def hi(name): print('hi', name)
>>> mock_hi = mock.Mock(wraps=hi)
The wrapped function is called by the mock.
>>> mock_hi('Bob')
hi Bob
But it's still a mock that remembers calls.
>>> mock_hi.call_args_list
[call('Bob')]
Recall that patch() will pass along extra kwargs to the Mock it makes, so you can use the wraps argument here too. For example,
>>> with mock.patch('builtins.float', wraps=float) as mock_float:
... x = float('inf')
... print(x) # If we hadn't wrapped, x would be a mock.
... print(mock_float.call_args_list)
...
inf
[call('inf')]

When using unittest.mock.patch, why is autospec not True by default?

When you patch a function using mock, you have the option to specify autospec as True:
If you set autospec=True then the mock with be created with a spec
from the object being replaced. All attributes of the mock will also
have the spec of the corresponding attribute of the object being
replaced. Methods and functions being mocked will have their arguments
checked and will raise a TypeError if they are called with the wrong
signature.
(http://www.voidspace.org.uk/python/mock/patch.html)
I'm wondering why this isn't the default behaviour? Surely we would almost always want to catch passing incorrect parameters to any function we patch?
The only clear way to explain this, is to actually quote the documentation on the downside of using auto-speccing and why you should be careful when using it:
This isn’t without caveats and limitations however, which is why it is
not the default behaviour. In order to know what attributes are
available on the spec object, autospec has to introspect (access
attributes) the spec. As you traverse attributes on the mock a
corresponding traversal of the original object is happening under the
hood. If any of your specced objects have properties or descriptors
that can trigger code execution then you may not be able to use
autospec. On the other hand it is much better to design your objects
so that introspection is safe [4].
A more serious problem is that it is common for instance attributes to
be created in the init method and not to exist on the class at
all. autospec can’t know about any dynamically created attributes and
restricts the api to visible attributes.
I think the key takeaway here is to note this line: autospec can’t know about any dynamically created attributes and restricts the api to visible attributes
So, to help being more explicit with an example of where autospeccing breaks, this example taken from the documentation shows this:
>>> class Something:
... def __init__(self):
... self.a = 33
...
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
As you can see, auto-speccing has no idea that there is an attribute a being created when creating your Something object.
There is nothing wrong with assigning a value to your instance attribute.
Observe the below functional example:
import unittest
from mock import patch
def some_external_thing():
pass
def something(x):
return x
class MyRealClass:
def __init__(self):
self.a = some_external_thing()
def test_thing(self):
return something(self.a)
class MyTest(unittest.TestCase):
def setUp(self):
self.my_obj = MyRealClass()
#patch('__main__.some_external_thing')
#patch('__main__.something')
def test_my_things(self, mock_something, mock_some_external_thing):
mock_some_external_thing.return_value = "there be dragons"
self.my_obj.a = mock_some_external_thing.return_value
self.my_obj.test_thing()
mock_something.assert_called_once_with("there be dragons")
if __name__ == '__main__':
unittest.main()
So, I'm just saying for my test case I want to make sure that the some_external_thing() method does not affect the behaviour of my unittest, so I'm just assigning my instance attribute the mock per mock_some_external_thing.return_value = "there be dragons".
Answering my own question many years later - another reason is speed.
Depending on how complex your object is, it can be that using autospec can slow your test down significantly. I've found this particularly when patching Django models.
The action of autospeccing itself can execute code, for example via the invocation of descriptors.
>>> class A:
... #property
... def foo(self):
... print("rm -rf /")
...
>>> a = A()
>>> with mock.patch("__main__.a", autospec=False) as m:
... pass
...
>>> with mock.patch("__main__.a", autospec=True) as m:
... pass
...
rm -rf /
Therefore, this is a problematic feature to enable by default and
is opt-in only.

python - Accessing objects mocked with patch

I've been using the mock library to do some of my testing. It's been great so far, but there are some things that I haven't completely understand yet.
mock provides a nice way of patching an entire method using patch, and I could access the patched object in a method like so:
#patch('package.module')
def test_foo(self, patched_obj):
# ... call patched_obj here
self.assertTrue(patched_obj.called)
My question is, how do I access a patched object, if I use the patch decorator on an entire class?
For example:
#patch('package.module')
class TestPackage(unittest.TestCase):
def test_foo(self):
# how to access the patched object?
In this case, test_foo will have an extra argument, the same way as when you decorate the method. If your method is also patched, it those args will be added as well:
#patch.object(os, 'listdir')
class TestPackage(unittest.TestCase):
#patch.object(sys, 'exit')
def test_foo(self, sys_exit, os_listdir):
os_listdir.return_value = ['file1', 'file2']
# ... Test logic
sys_exit.assert_called_with(1)
The arguments order is determined by the order of the decorators calls. The method decorator is called first, so it appends the first argument. The class decorator is outer, so it will add a second argument. The same applies when you attach several patch decorators to the same test method or class (i.e. the outer decorator goes last).

Categories