I'm using python mocks to assert that a particular object was created with the right arguments. This is how my code looks:
class Installer:
def __init__(foo, bar, version):
# Init stuff
pass
def __enter__(self):
return self
def __exit__(self, type, value, tb):
# cleanup
pass
def install(self):
# Install stuff
pass
class Deployer:
def deploy(self):
with Installer('foo', 'bar', 1) as installer:
installer.install()
Now, I want to assert that installer was created with the right arguments. This is the code I have so far:
class DeployerTest(unittest.TestCase):
#patch('Installer', autospec=True)
def testInstaller(self, mock_installer):
deployer = Deployer()
deployer.deploy()
# Can't do this :-(
mock_installer.__init__.assert_called_once_with('foo', 'bar', 1)
This is the error I get:
File "test_deployment.py", line .., in testInstaller
mock_installer.__init__.assert_called_once_with('foo', 'bar', 1)
AttributeError: 'function' object has no attribute 'assert_called_once_with'
Here is the fixed code (Call it test.py). Thanks, all!
import unittest
from mock import patch
class Installer:
def __init__(self, foo, bar, version):
# Init stuff
pass
def __enter__(self):
return self
def __exit__(self, type, value, tb):
# cleanup
pass
def install(self):
# Install stuff
pass
class Deployer:
def deploy(self):
with Installer('foo', 'bar', 1) as installer:
installer.install()
class DeployerTest(unittest.TestCase):
#patch('tests.test.Installer', autospec=True)
def testInstaller(self, mock_installer):
deployer = Deployer()
deployer.deploy()
# Can't do this :-(
# mock_installer.__init__.assert_called_once_with('foo', 'bar', 1)
# Try this instead
mock_installer.assert_called_once_with('foo', 'bar', 1)
So, the error message you are getting is actually because you are not checking your mock properly. What you have to understand here is that in your decorator you are ultimately saying that, the call to Installer will relturn a Mock object instead.
Therefore, for any call to Installer() with respect to where you are patching, the return value of that will call Mock() instead.
So, the assertion you actually want to check is simply at the mock_installer, and not the mock_installer.__init__.:
mock_installer.assert_called_once_with('foo', 'bar', 1)
Here is the modification made to your code:
class DeployerTest(unittest.TestCase):
#patch('Installer', autospec=True)
def testInstaller(self, mock_installer):
deployer = Deployer()
deployer.deploy()
mock_installer.assert_called_once_with('foo', 'bar', 1)
A little extra information to provide some more explanation, if you were testing now if install was called within your context manager, you have to realize here that you actually have to check inside your __enter__, so a structure would be like this:
For clarity sake create a mock_obj in your test method and:
mock_obj = mock_installer.return_value
Now, within your context manager, you will need to look inside the call to __enter__(). In case you don't know why this is, read up on context managers.
So, with that in mind, you simply perform your check as:
mock_obj.__enter__().install.assert_called_once_with()
The problem is that Installer(...) doesn't call Installer.__init__ directly; rather, because Installer is an instance of type, and type.__call__ is defined, you get Installer() being equivalent to
type.__call__(Installer, ...)
which results in a call to Installer.__new__(Installer, ...), whose return value x is passed to Installer.__init__ along with the original arguments.
All of which is to say that since the mock object you bind to Installer isn't an instance of type, none of the preceding applies. Installer(...) is simply a call to a mock object, so you check that that directly:
mock_installer.assert_called_once_with('foo', 'bar', 1)
Related
I would like to assert from a UT, TestRunner.test_run that some deeply nested function Prompt.run_cmd is called with the string argument "unique cmd". My setup besically resembles the following:
# Module application/engine/prompt.py
class Prompt:
def run_cmd(self, input):
pass
# Module: application/scheduler/runner.py
class Runner:
def __init__(self):
self.prompt = application.engine.prompt.Prompt()
def run(self):
self.prompt.run_cmd("unique cmd")
# Module tests/application/scheduler/runner_test.py
class TestRunner(unittest.TestCase):
...
def test_run(self):
# calls Runner.run
# Objective assert that Prompt.run is called with the argument "unique cmd"
# Current attempt below:
with mock.patch(application.engine.prompt, "run_cmd") as mock_run_cmd:
pass
Unfortunately my attempts to mock the Prompt.run_cmd fail with the error message
AttributeError: 'module' object has no attribute 'object'
If you wanted to patch a concrete instance, you could easily do this using mock.patch.object and wraps (see for example this question.
If you want to patch your function for all instances instead, you indeed have to use mock.patch. In this case you could only mock the class itself, as mocking the method would not work (because it is used on instances, not classes), so you cannot use wraps here (at least I don't know a way to do this).
What you could do instead is derive your own class from Prompt and overwrite the method to collect the calls yourself. You could then patch Prompt by your own implementation. Here is a simple example:
class MyPrompt(Prompt):
calls = []
def run_cmd(self, input):
super().run_cmd(input)
# we just add a string in the call list - this could be more sophisticated
self.__class__.calls.append(input)
class TestRunner(unittest.TestCase):
def test_run(self):
with mock.patch("application.engine.prompt.Prompt", MyPrompt) as mock_prompt:
runner = Runner()
runner.run()
self.assertEqual(["unique cmd"], mock_prompt.calls)
I have a python method like
import external_object
from external_lib1 import ExternalClass1
from external_lib2 import Hook
class MyClass(self):
def my_method(self):
ExternalClass.get('arg1') #should be mocked and return a specific value with this arg1
ExternalClass.get('arg2') #should be mocked and return a specific value with this arg2
def get_hook(self):
return Hook() # return a mock object with mocked method on it
def my_method(self):
object_1 = external_object.instance_type_1('args') # those are two different object instanciate from the same lib.
object_2 = external_object.instance_type_2('args')
object_1.method_1('arg') # should return what I want when object_1 mocked
object_2.method_2 ('arg') # should return what I want when object_2 mocked
In my test I would like to realise what I put in comments.
I could manage to do it, but every time it gets really messy.
I use to call flexmock for some stuff (by example ExternalClass.get('arg1') would be mock with a flexmock(ExternalClass).should_return('arg').with_args('arg') # etc...) but I'm tired of using different test libs to mock.
I would like to use only the mock library but I struggle to find a consistent way of doing it.
I like to use python's unittest lib. Concretely the unittest.mock which is a great lib to customize side effects and return value in unit tested functions.
They can be used as follows:
class Some(object):
"""
You want to test this class
external_lib is an external component we cannot test
"""
def __init__(self, external_lib):
self.lib = external_lib
def create_index(self, unique_index):
"""
Create an index.
"""
try:
self.lib.create(index=unique_index) # mock this
return True
except MyException as e:
self.logger.error(e.__dict__, color="red")
return False
class MockLib():
pass
class TestSome(unittest.TestCase):
def setUp(self):
self.lib = MockLib()
self.some = Some(self.lib)
def test_create_index(self):
# This will test the method returns True if everything went fine
self.some.create_index = MagicMock(return_value={})
self.assertTrue(self.some.create_index("test-index"))
def test_create_index_fail(self):
# This will test the exception is handled and return False
self.some.create_index = MagicMock(side_effect=MyException("error create"))
self.assertFalse(self.some.create_index("test-index"))
Put the TestSome() class file somewhere like your-codebase-path/tests and run:
python -m unittest -v
I hope it's useful.
I have a class with an expensive __init__ function. I don't want this function called from tests.
For the purpose of this example, I made a class that raises an Exception in __init__:
class ClassWithComplexInit(object):
def __init__(self):
raise Exception("COMPLEX!")
def get_value(self):
return 'My value'
I have a second class that constructs an instance of ClassWithComplexInit and uses it's function.
class SystemUnderTest(object):
def my_func(self):
foo = ClassWithComplexInit()
return foo.get_value()
I am trying to write some unit tests around SystemUnderTest#my_func(). The problem I am having is no matter how I try to mock ClassWithComplexInit, the __init__ function always gets executed and the exception is raised.
class TestCaseWithoutSetUp(unittest.TestCase):
#mock.patch('mypackage.ClassWithComplexInit.get_value', return_value='test value')
def test_with_patched_function(self, mockFunction):
sut = SystemUnderTest()
result = sut.my_func() # fails, executes ClassWithComplexInit.__init__()
self.assertEqual('test value', result)
#mock.patch('mypackage.ClassWithComplexInit')
def test_with_patched_class(self, mockClass):
mockClass.get_value.return_value = 'test value'
sut = SystemUnderTest()
result = sut.my_func() # seems to not execute ClassWithComplexInit.__init__()
self.assertEqual('test value', result) # still fails
# AssertionError: 'test value' != <MagicMock name='ClassWithComplexInit().get_value()' id='4436402576'>
The second approach above is one that I got from this similar Q&A but it didn't work either. It seemed to not run the __init__ function but my assertion fails because the result ends up being a mock instance as opposed to my value.
I also tried to configure a patch instance in the setUp function, using the start and stop functions as the docs suggest.
class TestCaseWithSetUp(unittest.TestCase):
def setUp(self):
self.mockClass = mock.MagicMock()
self.mockClass.get_value.return_value = 'test value'
patcher = mock.patch('mypackage.ClassWithComplexInit', self.mockClass)
patcher.start()
self.addCleanup(patcher.stop)
def test_my_func(self):
sut = SystemUnderTest()
result = sut.my_func() # seems to not execute ClassWithComplexInit.__init__()
self.assertEqual('test value', result) # still fails
# AssertionError: 'test value' != <MagicMock name='mock().get_value()' id='4554658128'>
This also seems to avoid my __init__ function but the value I set for get_value.return_value isn't being respected and get_value() is still returning a MagicMock instance.
How can I mock a class with an complicated __init__ which is instantiated by my code under test? Ideally, I would like a solution which works well for many unit tests within a TestCase class (e.g. Not needing to patch every test).
I am using Python version 2.7.6.
First, you need to use the same name you are patching to create foo, that is,
class SystemUnderTest(object):
def my_func(self):
foo = mypackage.ClassWithComplexInit()
return foo.get_value()
Second, you need to configure the correct mock object. You are configuring ClassWithComplexInit.get_value, the unbound method, but you need to configure ClassWithComplexInit.return_value.get_value, which is the Mock object that will be actually be called with foo.get_value().
#mock.patch('mypackage.ClassWithComplexInit')
def test_with_patched_class(self, mockClass):
mockClass.return_value.get_value.return_value = 'test value'
sut = SystemUnderTest()
result = sut.my_func() # seems to not execute ClassWithComplexInit.__init__()
self.assertEqual('test value', result) # still fails
I want to assert that one classmethod in a Python class calls another classmethod with a certain set of arguments. I would like the mocked classmethod to be "spec-ed", so it detects if it is called with the wrong number of arguments.
When I patch the classmethod using patch.object(.., autospec=True, ..), the classmethod is replaced with a NonCallableMagicMock and raises an error when I try to call it.
from mock import patch
class A(object):
#classmethod
def api_meth(cls):
return cls._internal_classmethod(1, 2, 3)
#classmethod
def _internal_classmethod(cls, n, m, o):
return sum(n, m, o)
with patch.object(A, '_internal_classmethod') as p:
print(type(p).__name__)
with patch.object(A, '_internal_classmethod', autospec=True) as p:
print(type(p).__name__)
produces the output:
MagicMock
NonCallableMagicMock
How can I get a spec-ed mock for _internal_classmethod when the class it belongs to is not mocked?
There's an outstanding bug report (google code link and python bug tracker link) to fix this issue. Until the fix gets incorporated, you can try the following, which worked for me [On 2.7, though I think it would also work in 3.x].
def _patched_callable(obj):
"Monkeypatch to allow autospec'ed classmethods and staticmethods."
# See https://code.google.com/p/mock/issues/detail?id=241 and
# http://bugs.python.org/issue23078 for the relevant bugs this
# monkeypatch fixes
if isinstance(obj, type):
return True
if getattr(obj, '__call__', None) is not None:
return True
if (isinstance(obj, (staticmethod, classmethod))
and mock._callable(obj.__func__)):
return True
return False
_patched_callable._old_func = mock._callable
mock._callable = _patched_callable
After the monkeypatch, you should be able to use mock.patch normally and have static- and class-methods patched properly.
Use spec in place of autospec, and set it directly.
with patch.object(A, '_internal_classmethod', spec=A._internal_classmethod) as p:
print(type(p).__name__)
gives me
MagicMock
for output.
I'm trying to start using unittest.mock's action/assert pattern instead of mox's record/replay/verify pattern.
# foo.py
def op_1(param):
pass
def op_2(param):
pass
def do_stuff(param_1, param_2):
global config
global log
try:
op_1(param_1)
if config.getboolean('section','option'):
op_2(param_2)
except:
log.error("an error occured")
And, here's an example of what my unittest file looks like.
# test_foo.py
class TestFoo(unittest.TestCase):
def test_do_stuff(self):
param_1 = None
param_2 = None
foo.config = MagicMock()
foo.config.getboolean('section','option', return_value = True)
foo.op_1 = MagicMock()
foo.op_2 = MagicMock()
do_stuff(param_1, param_2)
foo.op_1.assert_called_once_with(param_1)
foo.op_2.assert_called_once_with(param_2)
foo.config.getboolean.assert_called_once_with('section','option')
Does this test to verify the items below/am I using mock right?
do_stuff call returned without error
op_1 was called with param_1
op_2 was called with param_2
config parser object had been used, but the specific calls don't matter
It turns out that I was using the return_value wrong.
When I need a mock.Mock or mock.MagicMock object to return a value, it will need to always return that value, regardless of the arguments passed. Though, it might be nice to give different behavior based on arguments passed (possible feature request).
The way I completed this was:
foo.config.getboolean = mock.MagicMock(return_value = True)
And then I can do this:
self.assertGreaterThan(len(foo.config.mock_calls), 0)
self.assertGreaterThan(len(foo.config.getboolean(str(),str())), 0)