mock on function with optional arguments - python

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.

Related

Mocking functions in python called from a dictionary

I have a problem with the code below. I would like to mock the functions in different file for unit testing in the FUNCTION_MAPPING part.
import module.module2 as module_name
FUNCTION_MAPPING = {
1: module_name.foo,
2: module_name.foo2,
3: module_name.foo3
}
def my_func(number):
function_call = FUNCTION_MAPPING[number]
result = function_call()
return result
For some reason I can not mock those functions. I have tried every possible way that i had knowledge about. If possible i would like not to change the code above.
foo, foo2 and foo3 inner code can be anything print(1), print(2) etc
Code of the unit test:
#patch("module_of_the_code_above.module_name.foo",return_value="Test")
def test_my_func(self,mocked_foo):
result = my_func(1)
nose_tools.assert_equal(result,"Test")
Simple two options if you use MagicMock:
The issue with this case is that as you import your module the dictionary with the lookups is created and the references to the "module_name.foo" is made, so any patching/mocking at a global level will not affect that explicit mapping, you have to replace/wrap it on that structure.
....
# in you test manually replace the function mapping with either new
# functions you define, but I prefer MagicMock as it allows you to get
# all kinds of goodies.
# This is necessary as the dict for the function lookup is probably already
# initialize before any mocking can take place (as you include the module)
# and so even patching the function globally will not affect that lookup
# You can use MagicMock so that your original function is not used..
FUNCTION_MAPPING['1'] = mock.MagicMock()
FUNCTION_MAPPING['2'] = mock.MagicMock()
FUNCTION_MAPPING['3'] = mock.MagicMock()
# Or
# if you just want to spy/keep stats but still call the original function, you will
# need to do something like
FUNCTION_MAPPING['1'] = mock.Mock(wraps=FUNCTION_MAPPING['1')
# then you can all the great things that you can do with mock.
assert FUNCTION_MAPPING['1'].call_counts == 3
PS: did not have time to test/vet the this for exact syntax but hope this points you in the right direction.
You should assign the function(without brackets) and not the result of the fucntion - {1: func}
def hello():
print('Hello!')
def text(text):
print(text)
function_map = {
1: hello,
2: text
}
func1 = function_map[1]
func1()
func2 = function_map[2]
func2('ABC123')
Output:
Hello!
ABC123
When working with imports:
import math
function_map = {
'factorial': math.factorial,
'gcd': math.gcd
}
func1 = function_map['factorial']
print(func1(5))
func2 = function_map['gcd']
print(func2(5, 25))

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.

Python unittest check function call args

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.

Aliasing a class attribute to a function

Is it possible to do something like the following:
class foo():
def bar(): # a method that doesn't take any args
# slow calculation
return somefloat
b = bar # bar is a function but b just gives you the float attribute
f = foo()
f.b # returns somefloat but doesn't require the empty parentheses
I hope the example is clear since I'm not super clear on what the terminology is for what I want to do. My basic goal is to remove a bunch of parentheses for methods that don't have arguments to make the code cleaner to read.
The function is slow and rarely used so it would be easiest to calculate it real time rather than calculate it once ahead of time and store the variable.
Is this possible? Is it good practice? Is there a better way?
The standard way to achieve this is to use property, which is a decorator:
class Foo():
#property
def bar(self):
# slow calculation
return somefloat
f = Foo()
f.bar # returns somefloat but doesn't require the empty parentheses
A couple of things to notice:
You still need self in the method signature as usual, because sometimes you're going to need to refer to e.g. self.some_attribute inside the method. As you can see, that doesn't affect the use of the property at all.
There's no need to clutter your API with both a f.bar() method and a f.b property - it's better to decide what makes most sense for your class than offer a heap of different ways to do the same thing.
b = bar obviously wouldn't work. However a property would for the simplest "doesn't require the empty parentheses" ask of yours:
b = property(bar)
Now every access to f.b will call f.bar() "behind the curtains".
However this means that if you access f.b twice, f.bar() gets called twice, repeating the computation. If the repetition is irrelevant (i.e if the result doesn't change for repeated computations on the same object) you can do better ("caching" the result in f.b forever once it's first been computed) -- something like:
class foo(object):
def bar(self): # a method that doesn't take any args
# slow calculation
return somefloat
def _cache_bar(self):
result = self.bar()
setattr(self, 'b', result)
return result
b = property(_cache_bar)
By static method, but need to call by parentheses.
class foo(object):
#staticmethod
def bar(): # a method that doesn't take any args
# slow calculation
return "abc"
b = bar # bar is a function but b just gives you the float attribute
f = foo()
print f.b()
output:
$ python test.py
abc

Inheriting from unittest.TestCase for non-test functionality

I want to write a class to check sets using exactly the behavior that unittest.TestCase.assertEqual exhibits for testing set equality. It automatically prints a nice message saying which elements are only in the first set and which are only in the second set.
I realize I could implement similar behavior, but since it's already done nicely with unittest.TestCase.assertEqual, I'd prefer to just utilize that (so please no answers that say the unhelpful and already obvious (but not applicable in this case) advice "don't solve this with unittest.TestCase")
Here is my code for the SetChecker class:
import unittest
class SetChecker(unittest.TestCase):
"""
SetChecker(set1, set2) creates a set checker from the two passed Python set
objects. Printing the SetChecker uses unittest.TestCase.assertEqual to test
if the sets are equal and automatically reveal the elements that are in one
set but not the other if they are unequal. This provides an efficient way
to detect differences in possibly large set objects. Note that this is not
a unittest object, just a wrapper to gain access to the helpful behavior of
unittest.TestCase.assertEqual when used on sets.
"""
EQUAL_MSG = "The two sets are equivalent."
def __init__(self, set1, set2, *args, **kwargs):
assert isinstance(set1, set)
assert isinstance(set2, set)
super(self.__class__, self).__init__(*args, **kwargs)
try:
self.assertEqual(set1, set2)
self._str = self.EQUAL_MSG
self._str_lines = [self._str]
self._indxs = None
except AssertionError, e:
self._str = str(e)
self._str_lines = self._str.split('\n')
# Find the locations where a line starts with 'Items '.
# This is the fixed behavior of unittest.TestCase.
self._indxs = [i for i,y in enumerate(self._str_lines)
if y.startswith('Items ')]
def __repr__(self):
"""
Convert SetChecker object into a string to be printed.
"""
return self._str
__str__ = __repr__ # Ensure that `print` and __repr__ do the same thing.
def runTest(self):
"""
Required by any sub-class of unittest.TestCase. Solely used to inherit
from TestCase and is not implemented for any behavior.
"""
pass
def in_first_set_only(self):
"""
Return a list of the items reported to exist only in the first set. If
the sets are equivalent, returns a string saying so.
"""
return (set(self._str_lines[1:self._indxs[1]])
if self._indxs is not None else self.EQUAL_MSG)
def in_second_set_only(self):
"""
Return a list of the items reported to exist only in the second set. If
the sets are equivalent, returns a string saying so.
"""
return (set(self._str_lines[1+self._indxs[1]:])
if self._indxs is not None else self.EQUAL_MSG)
This works fine when I use it in IPython:
In [1]: from util.SetChecker import SetChecker
In [2]: sc = SetChecker(set([1,2,3, 'a']), set([2,3,4, 'b']))
In [3]: sc
Out[3]:
Items in the first set but not the second:
'a'
1
Items in the second set but not the first:
'b'
4
In [4]: print sc
Items in the first set but not the second:
'a'
1
Items in the second set but not the first:
'b'
4
In [5]: sc.in_first_set_only()
Out[5]: set(["'a'", '1'])
In [6]: sc.in_second_set_only()
Out[6]: set(["'b'", '4'])
But now I also want to write unit tests for this class. So I've made a TestSetChecker class. Here is that code:
from util.SetChecker import SetChecker
class TestSetChecker(unittest.TestCase):
"""
Test class for providing efficient comparison and printing of
the difference between to sets.
"""
def setUp(self):
"""
Create examples for testing.
"""
self.set1 = set([1, 2, 3, 'a'])
self.set2 = set([2, 3, 4, 'b'])
self.set3 = set([1,2])
self.set4 = set([1,2])
self.bad_arg = [1,2]
self.expected_first = set(['1', 'a'])
self.expected_second = set(['4', 'b'])
self.expected_equal_message = SetChecker.EQUAL_MSG
self.expected_print_string = (
"Items in the first set but not the second:\n'a'\n1\n"
"Items in the second set but not the first:\n'b'\n4")
def test_init(self):
"""
Test constructor, assertions on args, and that instance is of proper
type and has expected attrs.
"""
s = SetChecker(self.set1, self.set2)
self.assertIsInstance(s, SetChecker)
self.assertTrue(hasattr(s, "_str"))
self.assertTrue(hasattr(s, "_str_lines"))
self.assertTrue(hasattr(s, "_indxs"))
self.assertEqual(s.__repr__, s.__str__)
self.assertRaises(AssertionError, s, *(self.bad_arg, self.set1))
def test_repr(self):
"""
Test that self-printing is correct.
"""
s1 = SetChecker(self.set1, self.set2)
s2 = SetChecker(self.set3, self.set4)
self.assertEqual(str(s1), self.expected_print_string)
self.assertEqual(str(s2), self.expected_equal_message)
def test_print(self):
"""
Test that calling `print` on SetChecker is correct.
"""
s1 = SetChecker(self.set1, self.set2)
s2 = SetChecker(self.set3, self.set4)
s1_print_output = s1.__str__()
s2_print_output = s2.__str__()
self.assertEqual(s1_print_output, self.expected_print_string)
self.assertEqual(s2_print_output, self.expected_equal_message)
def test_in_first_set_only(self):
"""
Test that method gives list of set elements found only in first set.
"""
s1 = SetChecker(self.set1, self.set2)
s2 = SetChecker(self.set3, self.set4)
fs1 = s1.in_first_set_only()
fs2 = s2.in_first_set_only()
self.assertEqual(fs1, self.expected_first)
self.assertEqual(fs2, self.expected_equal_message)
def test_in_second_set_only(self):
"""
Test that method gives list of set elements found only in second set.
"""
s1 = SetChecker(self.set1, self.set2)
s2 = SetChecker(self.set3, self.set4)
ss1 = s1.in_second_set_only()
ss2 = s2.in_second_set_only()
self.assertEqual(ss1, self.expected_second)
self.assertEqual(ss2, self.expected_equal_message)
if __name__ == "__main__":
unittest.main()
As far as I can tell, TestSetChecker has no differences from the many other unit test classes that I write (apart from the specific functionality it is testing for).
Yet, I am seeing a very unusual __init__ error when I try to execute the file containing the unit tests:
EMS#computer ~/project_dir/test $ python TestSetChecker.py
Traceback (most recent call last):
File "TestSetChecker.py", line 84, in <module>
unittest.main()
File "/opt/python2.7/lib/python2.7/unittest/main.py", line 94, in __init__
self.parseArgs(argv)
File "/opt/python2.7/lib/python2.7/unittest/main.py", line 149, in parseArgs
self.createTests()
File "/opt/python2.7/lib/python2.7/unittest/main.py", line 155, in createTests
self.test = self.testLoader.loadTestsFromModule(self.module)
File "/opt/python2.7/lib/python2.7/unittest/loader.py", line 65, in loadTestsFromModule
tests.append(self.loadTestsFromTestCase(obj))
File "/opt/python2.7/lib/python2.7/unittest/loader.py", line 56, in loadTestsFromTestCase
loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames))
TypeError: __init__() takes at least 3 arguments (2 given)
The directory with the Python unittest source code is read-only in my environment, so I can't add pdb or even print statements there to see what testCaseClass or testCaseNames are at this point where some __init__ fails.
But I can't see any places in my code where I'm failing to provide needed arguments to any __init__ method. I'm wondering if this has something to do with some behind-the-scenes magic with classes that inherit from unittest and with the fact that I'm importing and instantiating a class (SetChecker) within the file that is to be executed for unit tests.
Maybe it checks for all classes in the existing namespace that inherit from TestCase? If so, how do you unit-test the unit tests?
I also tried to first make SetChecker inherit from object and tried to use TestCase like a mix-in, but that created lots of MRO errors and seemed more headache than it was worth.
I've tried searching for this but it's a difficult error to search for (since it does not appear to be a straightforward problem with __init__ arguments).
I was able to work around this by making SetChecker inherit from object only, and then inside of SetChecker providing an internal class that inherits from unittest.TestCase.
The problem is that unittest.main inspects the whole namespace of the module it is run from. Any class it finds in that module that inherits from unittest.TestCase will get the full test-suite treatment (it will try to construct instances of the class for each test_ method it can find, or just for runTest if it finds no test_ methods).
In my case, since the set arguments are required, whatever it is that unittest.main is doing, it's passing some argument (probably the name of the function to treat as the test, in this case the string "runTest") but failing to pass the second required argument. Even if this worked with the signature of my class (e.g. suppose that I replaced the two distinct arguments set1 and set2 with a tuple of 2 sets), it would then immediately fail once it tried to do set operations with that string.
There doesn't appear to be an easy way to tell unittest.main to ignore a certain class or classes. So, by making SetChecker just an object that has a TestCase inside of it, unittest.main no longer finds that TestCase and no longer cares.
There was one other bug: in my test_init function, I use assertRaises which expects a callable, but had never given my SetChecker class a __call__ function.
Here's the modification to the SetChecker class that fixed this for me:
class SetChecker(object):
"""
SetChecker(set1, set2) creates a set checker from the two passed Python set
objects. Printing the SetChecker uses unittest.TestCase.assertEqual to test
if the sets are equal and automatically reveal the elements that are in one
set but not the other if they are unequal. This provides an efficient way
to detect differences in possibly large set objects. Note that this is not
a unittest object, just a wrapper to gain access to the helpful behavior of
unittest.TestCase.assertEqual when used on sets.
"""
EQUAL_MSG = "The two sets are equivalent."
class InternalTest(unittest.TestCase):
def runTest(self): pass
def __init__(self, set1, set2):
assert isinstance(set1, set)
assert isinstance(set2, set)
self.int_test = SetChecker.InternalTest()
try:
self.int_test.assertEqual(set1, set2)
self._str = self.EQUAL_MSG
self._str_lines = [self._str]
self._indxs = None
except AssertionError, e:
self._str = str(e)
self._str_lines = self._str.split('\n')
# Find the locations where a line starts with 'Items '.
# This is the fixed behavior of unittest.TestCase.
self._indxs = [i for i,y in enumerate(self._str_lines)
if y.startswith('Items ')]
#classmethod
def __call__(klass, *args, **kwargs):
"""
Makes the class callable such that calling it like a function is the
same as constructing a new instance.
"""
return klass(*args, **kwargs)
# Everything else below is the same...

Categories

Resources