pytest: unittest error object has no attribute 'assert_called_once_with - python

I am writing unit test in pytest and getting error on assert_called_once_with.
I tired to use same way as been shown in the pytest documentation but seems I am missing something.
# Class which I am trying to mock. (./src/Trading.py)
class BaseTrade:
def __init__(self, name):
self.name = name
class Trade(BaseTrade):
def __init__ (self, name):
BaseTrade.__init__(self, name)
def get_balance(self, value):
# do calculation and return some value
# for demo purpose hard-coding it
return value * 10
#unit test (./unitest/test_test.py
import mock
import unittest
import sys
sys.path.append("../src")
import Trading
class TestTradeClass(unittest.TestCase):
#classmethod
def setUpClass(self):
self.expected_balance = 100
#classmethod
def tearDownClass(self):
pass
def test_trade(self):
with mock.patch.object(Trading.Trade, 'get_balance', new = lambda self, x: (x * 10) ) as mock_method:
obj = Trading.Trade("AAPL")
value = obj.get_balance(10)
assert value == 100
mock_method.assert_called_once_with(100)
Error on mock_method.assert_called_once_with(100)
AttributeError: 'function' object has no attribute 'assert_called_once_with'

I'm now of the belief you want side_effect. How is this? One file, assume test.py:
#!/usr/bin/env python
import unittest
import mock
class BaseTrade:
def __init__(self, name):
self.name = name
class Trade(BaseTrade):
def __init__(self, name):
BaseTrade.__init__(self, name)
def get_balance(self, value):
# do calculation and return some value
# for demo purpose hard-coding it
return value * 10
class TestTradeClass(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.expected_balance = 100
def test_trade(self):
# Without mock
obj = Trade("AAPL")
value = obj.get_balance(10)
assert value == 100
# With Trade.get_balance param patched
with mock.patch.object(
Trade, 'get_balance', side_effect=lambda value: value * 11
) as mock_method:
obj = Trade("AAPL")
value = obj.get_balance(10)
assert value == 110
mock_method.assert_called_once_with(10)
if __name__ == "__main__":
unittest.main()
chmod +x test.py
./test.py
Output:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Explanation:
Use side_effect instead of new
Combined to one file to make it easier
removing Trading.Trade
#classmethod to use cls and not self.
mock_method.assert_called_once_with(10), as side_effect cares about the value passed via obj.get_balance(10) and exists to alter the output.
Closer? If not can you clarify what you're trying to mock?

It's not easy to tell, but if Trading.Trade.get_method() is actually a vanilla function and not a method - you may need unittest.mock.create_autospec()
Are any of these of assistance?
'function' object has no attribute 'assert_called_once_with'
Python3 mock replace function with another function
It's not clear whaat Trading.Trade is.
If by chance, Trading is a class that has a self.trade = Trade(...) inside, your question would be substantially different. You'd need to get in deeper, patching Trade.get_method, then. You'd likely want to import Trade from the same module class Trading uses it (e.g. from .trading import Trading, Trade) - not from where Trade is declared from - then patch the Trade.get_method.

Related

How to set return value of method of mock.patch result?

I have a test function which is getting a mocked class via a #mock.patch decorator. Within the function, I want to force a return value of one of the method (which is needed for the test to succeed) as well as make assertions on another of the methods; these methods are exercised by calling a free function in the class's module.
Example module bla.py:
class Tool:
def __init__(self):
self.items = list()
def m1(self, val):
self.items.append(val)
def act(self):
return len(self.items)
def exercise_tool():
toolio = Tool()
for i in range(2):
toolio.m1(i)
return toolio.act() <= 2
and its test:
from unittest import mock
import bla
#mock.patch("bla.Tool")
def test_exercise_tool(mtool):
mtool.act.return_value = 3
assert not bla.exercise_tool() # verify the mocked return value
mtool.m1.assert_was_called()
The test fails with TypeError: '<=' not supported between instances of 'MagicMock' and 'int'.
So how do I get this test to pass?

DI with unit tests called from elsewhere in application?

I am attempting to do something similar to the following:
import unittest
class foo:
one = 1
two = 1
class bar:
one = 2
two = 2
class my_test(unittest.TestCase):
def __init__(self, di_source):
self.di = di_source
print 'initing my_test'
def setUp(self):
print 'setting up!'
def tearDown(self):
print 'tearing down :('
def test_case_one(self):
self.assertEqual(self.di.one,1)
def test_case_two(self):
self.assertEqual(self.di.two, 2)
di_one = foo()
di_two = bar()
# called from elsewhere in my application
test_one = my_test(di_one).run()
test_one = my_test(di_two).run()
My goal is to:
Be able to call run() on a test suite
Provide a DI container at runtime to that test suite
Take advantage of the setUp and tearDown functionality provided by the unit test framework
However, it seems when I attempt to do this that the unittest framework doesn't like my constructor:
AttributeError: 'my_test' object has no attribute '_testMethodName'
Is there a better way to structure this example to avoid this problem?
How about using something like this:
This allows you to create shared resources for a single suite, pass the resource to all unittests, and then test the object with multiple methods.
'''Example using a shared resource in a unittest'''
import unittest
def callable_function():
'''Generic callable_function, this should actually be connected to an object constructor or something else''
return {'a': 3}
class MyTest(unittest.TestCase):
'''Custom unittest test case'''
def __init__(self, resource, method_name):
super(MyTest, self).__init__(method_name)
self._resource = resource
def test_getitem(self):
'''Test getting item'''
self.assertEquals(self._resource['a'], 3)
def test_setitem(self):
'''Test getting item'''
self._resource['b'] = 2
self.assertEquals(self._resource['b'], 2)
def test_mutable(self):
'''Test changes persist across tests'''
self.assertEquals(self._resource['b'], 2)
def run_suite():
'''Run complete unittest suite'''
suite = unittest.TestSuite()
item = callable_function()
suite.addTests([
MyTest(item, 'test_getitem'),
MyTest(item, 'test_setitem'),
MyTest(item, 'test_mutable'),
])
runner = unittest.TextTestRunner()
runner.run(suite)
if __name__ == '__main__':
run_suite()
EDIT: If you need to discover methods on the fly, you can do the following:
import inspect
def get_tests(cls):
return [k for k, v in cls.__dict__.items() if k.startswith('test') and inspect.ismethod(v)]
for name in get_tests(MyTest):
suite.addTest(MyTest(resource, name))
The idea is simple: override the __init__ method so it takes a resource and method name, bind the resource to the class, and initialize the TestCase as normal.
When you run the test, just use the bound resource.

What is the right way to test callback invocation using Python unittest?

I have an application code that looks like the following.
# Filename: app.py
class Foo:
def __init__(self):
self.callback = None
def set_handler(self, callback):
self.callback = callback
def run(self, details):
name, age = details.split('/')
if age.isdigit():
age = int(age)
else:
age = -1
return self.callback(name, age)
As you can see, it offers a set_handler method to set a callback. The callback must later be invoked with two arguments: a string and an integer. I am trying to ensure this in a unittest.
# Filename: test_app.py
import unittest
from app import Foo
class AppTest(unittest.TestCase):
def f(self, a, b):
# This callback should get called with the first argument as
# string and the second argument as integer
return repr(a) + ',' + repr(b)
def test_callback(self):
foo = Foo()
foo.set_handler(self.f)
self.assertEqual(foo.run('John/20'), "'John',20")
self.assertEqual(foo.run('John/xyz'), "'John',-1")
if __name__ == '__main__':
unittest.main()
This unit test succeeds. But I don't think my way of testing is robust. This unit test is basically a hack because I don't know how to correctly test if a callback has been invoked with the right type of arguments. What I find weird about it is that AppTest's f method is sharing the responsibility of type checking by attempting to return a value which are composed of repr() of the arguments, and this is not at all robust to say the least.
Could you please help me? Is it possible to relieve the f method of the responsibility of testing the types?
EDIT:
Try using unittest.mock (standard library on Python 3.3). It allows you to assert how methods were called. For example:
import unittest
from unittest.mock import Mock
from app import Foo
class AppTest(unittest.TestCase):
def test_callback(self):
foo = Foo()
f = Mock()
foo.set_handler(f)
foo.run('John/20')
f.assert_called_with('John', 20)
foo.run('John/xyz')
f.assert_called_with('John', -1)
if __name__ == '__main__':
unittest.main()

Use alias for a property in Python at module level

I have the following in Python 2.7:
class MyClass(object):
...
#property
def my_attr(self):
...
#my_attr.setter
def my_attr(self, value):
...
I use getter/setter so that I can do some logic in there.
Then I can call:
import myModule
test = myModule.MyClass()
test.my_attr = 9
I would like to use an alias at the module level so that I can do something like that:
import myModule
myModule.my_attr = 9
Is there a way to do that?
Yes, absolutely; the key is that modules are themselves objects. First you need to make MyClass subclass the module type:
from types import ModuleType
class MyClass(ModuleType):
...
Then you replace the current module with an instance of MyClass:
import sys
sys.modules[__name__] = MyClass(__name__)
Note that this can be pretty confusing to static analysers and to people reading your code.
To provide a special handling for some attributes of a module, you could define a proxy class that does the special handling and delegates the rest to the original module object:
"""
>>> import property_on_module
>>> property_on_module.attr = 1
set attr property
>>> property_on_module.attr
get attr property
1
"""
import sys
class Module(object):
def __init__(self, module):
self.__module = module
def __getattr__(self, name):
return getattr(self.__module, name)
#property
def attr(self):
print("get attr property")
return self.__attr
#attr.setter
def attr(self, value):
print("set attr property")
self.__attr = value
if __name__ == "__main__": # test if run as a script
import doctest
sys.exit(doctest.testmod().failed)
else: # normal import, use `Module` class to provide `attr` property
sys.modules[__name__] = Module(sys.modules[__name__])
__getattr__ might not be enough; you could define __getattribute__/__setattr__ in this case e.g., quickdraw.py (based on sh.py).

How do you mock patch a python class and get a new Mock object for each instantiation?

OK,
I know this is mentioned in the manual, and probably has to do with side_effect and/or return_value, but a simple, direct example will help me immensely.
I have:
class ClassToPatch():
def __init__(self, *args):
_do_some_init_stuff()
def some_func():
_do_stuff()
class UUT():
def __init__(self, *args)
resource_1 = ClassToPatch()
resource_2 = ClassToPatch()
Now, I want to unit test the UUT class, and mock the ClassToPatch. Knowing the UUT class will instantiate exactly two ClassToPatch objects, I want the Mock framework to return a new Mock object for each instantiation, so I can later assert calls on each separately.
How do I achieve this using the #patch decorator in a test case? Namely, how to fix the following code sample?
class TestCase1(unittest.TestCase):
#patch('classToPatch.ClassToPatch',autospec=True)
def test_1(self,mock1,mock2):
_assert_stuff()
Here's a quick'n'dirty example to get you going:
import mock
import unittest
class ClassToPatch():
def __init__(self, *args):
pass
def some_func(self):
return id(self)
class UUT():
def __init__(self, *args):
resource_1 = ClassToPatch()
resource_2 = ClassToPatch()
self.test_property = (resource_1.some_func(), resource_2.some_func())
class TestCase1(unittest.TestCase):
#mock.patch('__main__.ClassToPatch', autospec = True)
def test_1(self, mock1):
ctpMocks = [mock.Mock(), mock.Mock()]
ctpMocks[0].some_func.return_value = "funky"
ctpMocks[1].some_func.return_value = "monkey"
mock1.side_effect = ctpMocks
u = UUT()
self.assertEqual(u.test_property, ("funky", "monkey"))
if __name__ == '__main__':
unittest.main()
I've added test_property to UUT so that the unit test does something useful. Now, without the mock test_property should be a tuple containing the ids of the two ClassToPatch instances. But with the mock it should be the tuple: ("funky", "monkey").
I've used the side_effect property of the mock object so that a different instance of ClassToPatch is returned on each call in the UUT initialiser.
Hope this helps.
Edit: Oh, by the way, when I run the unit test I get:
.
----------------------------------------------------------------------
Ran 1 test in 0.004s
OK
Here is another version which is more generic to handle any number of instances created:
class TestUUT:
def test_init(self, mocker):
class MockedClassToPatchMeta(type):
static_instance = mocker.MagicMock(spec=ClassToPatch)
def __getattr__(cls, key):
return MockedClassToPatchMeta.static_instance.__getattr__(key)
class MockedClassToPatch(metaclass=MockedClassToPatchMeta):
original_cls = ClassToPatch
instances = []
def __new__(cls, *args, **kwargs):
MockedClassToPatch.instances.append(
mocker.MagicMock(spec=MockedClassToPatch.original_cls))
MockedClassToPatch.instances[-1].__class__ = MockedClassToPatch
return MockedClassToPatch.instances[-1]
mocker.patch(__name__ + '.ClassToPatch', new=MockedClassToPatch)
UUT()
# since your original code created two instances
assert 2 == len(MockedClassToPatch.instances)
If you need more thorough validation for each instance you can access MockedClassToPatch.instances[0] or MockedClassToPatch.instances[1].
I've also created a helper library to generate the meta class boilerplate for me. To generate the needed code for your example I wrote:
print(PytestMocker(mocked=ClassToPatch, name=__name__).mock_classes().mock_classes_static().generate())

Categories