Python unittest check function call args - python

I am developing unit tests to an existing library, and I would like to test if the arguments a function is called with match certain criteria. In my case the function to test is:
class ...
def function(self):
thing = self.method1(self.THING)
thing_obj = self.method2(thing)
self.method3(thing_obj, 1, 2, 3, 4)
For the unit tests I have patched the methods 1, 2 and 3 in the following way:
import unittest
from mock import patch, Mock
class ...
def setUp(self):
patcher1 = patch("x.x.x.method1")
self.object_method1_mock = patcher1.start()
self.addCleanup(patcher1.stop)
...
def test_funtion(self)
# ???
In the unit test I would like to extract the arguments 1, 2, 3, 4 and compare them e.g. see if the 3rd argument is smaller than the fourth one ( 2 < 3). How would I go on about this with mock or another library?

You can get the most recent call arguments from a mock using the call_args attribute. If you want to compare the arguments of the self.method3() call, then you should be able to do something like this:
def test_function(self):
# Call function under test etc.
...
# Extract the arguments for the last invocation of method3
arg1, arg2, arg3, arg4, arg5 = self.object_method3_mock.call_args[0]
# Perform assertions
self.assertLess(arg3, arg4)
More info here on call_args and also call_args_list.

Related

mock on function with optional arguments

I want to write a test for "A" function which calls another "B" function in it. B function looks like this:
def Bfuncion(self, city: Optional[str], name: Optional[str]):
in A function I use B function two times, first time I use it in this way:
cities = self.Bfunction(name=None, city="ny", page=page)
and the second time in this way:
cities = self.Bfunction(name="Tom", city=None, page=page)
Now I want to write a test of A function, I would try this if there was only one use of B function:
mocker.patch(
"Path.To.My.Function",
return_value=[
{"someReturnValue"},
{"someReturnValue"},
{"someReturnValue"},
],
How do I write call mocker(), as I use B function with different arguments in each call.
Use the side_effect keyword argument, not the return_value keyword argument.
>>> from unittest.mock import Mock
>>> m = Mock(return_value=[1,2,3])
>>> m()
[1, 2, 3]
>>> m = Mock(side_effect=[1,2,3])
>>> m()
1
>>> m()
2
>>> m()
3
(patch, of course, simply passes keyword arguments it does not itself recognize to Mock to configure the object that is used by the patched name.)
This requires you to know ahead of time the order of calls that will be made. If you need more flexibility, patch the name with a custom function that behaves the way you want, instead of a Mock object.
def my_func(self, name, city, page):
...
mocker.patch(
"Path.To.My.Function",
new=my_func
)
I try to suggest you a solution for your test; I don't know if it is suited for your need because I have tried to guess some details that you have omitted in your question.
The production code
I suppose that your production code is contained in a file called functions.py and the content of functions.py is the following:
from typing import Optional
class MyClass:
def Bfunction(self, city: Optional[str], name: Optional[str]):
return city
def Afunction(self):
cities = self.Bfunction(name=None, city="ny", page='page')
print(cities)
cities = self.Bfunction(name="Tom", city=None, page='page')
print(cities)
Note that in functions.py:
I have defined a class because I have seen that your definition of the Bfunction contains the self argument.
The Afunction executes the two calls of Bfunction that you have write in your question.
The test file
The test file (which doesn't use pytest but only the module unittest) is the following:
import unittest
from unittest import mock
import functions
class MyTestCase(unittest.TestCase):
def test_something(self):
sut = functions.MyClass()
with mock.patch.object(sut, 'Bfunction') as mock_b:
# set values returned by Bfunction in the first and in the second call
mock_b.side_effect = ['cityA', 'cityB']
sut.Afunction()
# here I verify that Bfunction is called 2 times
self.assertEqual(2, mock_b.call_count)
mock_b.assert_called_with(city=None, name='Tom', page='page')
mock_b.assert_called_with(name="Tom", city=None, page='page')
if __name__ == '__main__':
unittest.main()
The output of test file execution
The output of the test file execution is:
cityA
cityB
.
----------------------------------------------------------------------
Ran 1 test in 0.002s
OK
The output showes:
Afunction calls exactly two times the Bfunction
the test verify the arguments passed to Bfunction
the presence of cityA and cityB in the output show you how to select different values returned by Bfunction (you have to use side_effect and not return_value).
I hope that this answer is useful for you.

How to get the arguments from a method call on a mocked object?

I have a unit test where the setup mocks a client like so:
def setUp(self):
self.mock_client = mock.patch.object(module_name, 'ClientClassName', autospec=True).start()
Then in my test I have a faked return value:
def myTest(self):
self.mock_client.my_method.return_value = ...
Now I want to get the arguments that my_method was called with, however I've been tearing my hair out trying to access them. It seems that I can't just do:
mock_args, mock_kwargs = self.mock_client.my_method.call_args
This gives me back First off why doesn't this work? I did make a little headway and found that:
self.mock_client.method_calls[0]
will give me back a call object that looks like call().my_method(...the arguments), but I have been trying for hours to get access to the individual arguments and cant seem to do it. Where am I going wrong?
Call args are just accessed with subscription on the mock.call object, i.e. __getitem__.
>>> from unittest.mock import MagicMock
>>> m = MagicMock()
>>> m(123, xyz="hello")
<MagicMock name='mock()' id='140736989479888'>
>>> m("another call")
<MagicMock name='mock()' id='140736989479888'>
>>> m.call_args_list
[call(123, xyz='hello'), call('another call')]
>>> m.call_args_list[0][0]
(123,)
>>> m.call_args_list[0][1]
{'xyz': 'hello'}
Item 0 will be a tuple of args, and item 1 will be a dict of kwargs. Attribute access also works, like a namedtuple (item 0 is attribute "args", and item 1 is attribute "kwargs"). If you only need to access the most recent call, you can use call_args instead of call_args_list.
Note that accessing the call args items directly is usually not required, you can use an assertion against another call instance in the tests:
>>> from unittest.mock import call
>>> m(k=123)
<MagicMock name='mock()' id='140736989479888'>
>>> assert m.call_args == call(k=123) # pass
>>> assert m.call_args == call(k=124) # fail
AssertionError
Or an even higher level, you can use m.assert_has_calls on the mock directly.
When mocking methods, whether the mock calls have self or not can be influenced by autospec:
>>> from unittest import mock
>>> class A(object):
... def f(self, *args, **kwargs):
... pass
...
>>> with mock.patch("__main__.A.f") as m:
... a = A()
... a.f('without autospec', n=1)
...
>>> m.call_args
call('without autospec', n=1)
>>> with mock.patch("__main__.A.f", autospec=True) as m:
... a = A()
... a.f('with autospec', n=2)
...
>>> m.call_args
call(<__main__.A object at 0x7fffe3d4e6a0>, 'with autospec', n=2)
This is discussed in more detail in the docs here.
In addition to wim's answer, you can actually dig down fairly deeply into this arguments, although sometimes you seem to find a string instead of a real object.
The main point to understand is that when you iterate through my_mock.call_args_list you get objects of type unittest.mock._Call. These can indeed be compared to call(...) objects which you have created yourself. But that's not all.
unittest.mock._Call is itself iterable, and consists of 2 elements: one is a tuple, the other is a dict. These are none other than the *args and **kwargs passed to the mock method.
Given that this is, as far as I can make out, completely undocumented in the docs, I suppose it is not beyond the bounds of possibility that this could break one day. It does often prove handy to know though, in my experience.

Clean way to test that a function has been applied to each item of a collection?

I want to test a function similar to the following:
def some_function_under_test(some_list_type_arg: List):
map(some_other_function, some_list_type_arg)
What is a good and clean way to unittest this?
I am going to mock the map function
assert map_mock.called_once_with(...)
but what if the function will be written this way
for i in some_list_type_arg:
some_other_function(i)
How do I test this function independently from its implementation, i.e. not tying the test to the map function?
You can assert that some_other_function was called on each element by mocking it with a mock that just calls the original function, e.g.:
import unittest
from mock import patch, Mock, call
def some_other_function(x):
return 2 * x
def some_function_under_test(some_list_type_arg):
return map(some_other_function, some_list_type_arg)
class Tests(unittest.TestCase):
def test_thing(self):
with patch('__main__.some_other_function', Mock(side_effect=some_other_function)) as other_mock:
self.assertEqual(list(some_function_under_test([1, 2, 3])),
[2, 4, 6])
self.assertEqual(other_mock.call_args_list,
[call(1), call(2), call(3)])
unittest.main()

Replacing keyword argument in pytest

I am doing some testing and I would like to mock one argument of a function.
For example I got function like:
def foo(arg1, arg2):
'do something'
and call of that function:
foo(1, 2)
I would like to monkeypatch it somehow to use 3 instead of 2. Is that possible?
tried something like:
monkeypatch.setattr('foo', partial(foo, arg2= 3))
But I got type error: foo() got multiple values for keyword argument 'arg2'
Any idea how to solve this?
You can simply alias the function:
old_foo = monkeypatch.foo
def foo(arg1, arg2):
return old_foo(arg1, 3)
monkeypatch.foo = foo

How work pre-defined descriptors in functions?

Python functions have a descriptors. I believe that in most cases I shouldn't use this directly but I want to know how works this feature? I tried a couple of manipulations with such an objects:
def a():
return 'x'
a.__get__.__doc__
'descr.__get__(obj[, type]) -> value'
What is the obj and what is the type?
>>> a.__get__()
TypeError: expected at least 1 arguments, got 0
>>> a.__get__('s')
<bound method ?.a of 's'>
>>> a.__get__('s')()
TypeError: a() takes no arguments (1 given)
Sure that I can't do this trick with functions which take no arguments. Is it required just only to call functions with arguments?
>>> def d(arg1, arg2, arg3):
return arg1, arg2, arg3
>>> d.__get__('s')('x', 'a')
('s', 'x', 'a')
Why the first argument taken directly by __get__, and everything else by returned object?
a.__get__ is a way to bind a function to an object. Thus:
class C(object):
pass
def a(s):
return 12
a = a.__get__(C)
is the rough equivalent of
class C(object):
def a(self):
return 12
(Though it's not a good idea to do it this way. For one thing, C won't know that it has a bound method called a, which you can confirm by doing dir(C). Basically, the __get__ does just one part of the process of binding).
That's why you can't do this for a function that takes no arguments- it must take that first argument (traditionally self) that passes the specific instance.

Categories

Resources