Avoid executing __init__ of mocked class - python

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

Related

How do you invoke a method on a class instance by string name?

I am trying to invoke a method on a class dynamically using a String for the class name and a String for the method name. I am using getattr then invoking a method on the class. You'll have to forgive me if I am way off, I am kind of new to Python.
class mock:
def __init__(self):
pass
def create(self):
print('hello world?')
return 'hello world'
then creating the instance and invoking via :
module = importlib.import_module('xyz.module')
instance = getattr(module, 'mock')
invoke = getattr(instance, 'create')
result = invoke()
print(result)
The result is something like <object object at 0x10943ccd0>. "hello world?" is never printed. What am I doing wrong?
You have missed a step. The instance variable you have isn't actually an instance, it's the class mock itself. You need to call it to get an instance. Try something like this:
module = importlib.import_module('xyz.module')
klass = getattr(module, 'mock') # rename this variable (avoiding keywords)
instance = klass() # and call the class to create an instance
method = getattr(instance, 'create') # also renamed here, for clarity
result = method() # previously, this would have been an error (missing self argument)
print(result) # now you should get "hello world" printed twice (once with a ?)
As a note, PEP 8 naming conventions would have helped you avoid the issue here. If you'd used the name Mock instead of mock for the class, it might have been a bit more obvious what kind of thing you had, after importing and getattring it.
You don't need getattr at all; the instance returned by import_module is the same thing that would be implicitly bound to xyz.module had you used an import statement.
module = importlib.import_module('xyz.module')
result = module.mock().create()
assert result == "hello world"
Note that the above creates an instance of mock on which to call create, rather than accessing mock.create directly. If you have variables containing the name of the class and the method, you still need to do that, only using getattr this time.
module = importlib.import_module('xyz.module')
cls_name = 'mock'
method_name = 'create'
cls = getattr(module, 'mock')
instance = cls()
invoke = getattr(instance, 'create')
result = invoke()
assert result == "hello world"

access python unittest magicmock return value

I'm using python 3.9.2 with unittest and mock to patch out a class.
My code under test instantiates an object of the class and mock returns a MagicMock object as the instance.
My question is, can I access that object from my test code?
I can see the call that instantiates the class in the mock_calls list, but cannot find a way of accessing the instance that is returned from that call.
The reason I need to access the instance is that my code under test attaches attributes to the instance rather than call methods on it. It is easy to test method calls, but is there a direct way to test attributes?
Upon investigation I found that there was only a single instance of a MagicMock being created and returned each time I instantiated my class. This behaviour was not convenient for me due to the attributes that I add to the class.
I created the following test aid to support my needs. This is not general-purpose but could be adapted for other circumstances.
class MockMyClass():
"""mock multiple MyClass instances
Note - the code under test must add a name attribute to each instance
"""
def __init__(self):
self.myclass = []
def factory(self, /, *args, **kwargs):
"""return a new instance each time called"""
new = mock.MagicMock()
# override __enter__ to enable the with... context manager behaviour
# for convenience in testing
new.__enter__ = lambda x: new
self.myclass.append(new)
return new
def __getitem__(self, key: str) -> None:
"""emulate a dict by returning the named instance
use as
mockmyclass['name'].assert_called_once()
or
with mockmyclass['name'] as inst:
inst.start().assert_called_once()
"""
# Important - the code under test gives the instance a name
# attribute and this relies on that attribute so is not
# general purpose
wanted = [t for t in self.myclass if t.name == key]
if not wanted:
names = [t.name for t in self.myclass]
raise ValueError(f'no timer {key} in {names}')
return wanted[0]
class TestBehaviour(unittest.TestCase):
def setUp(self):
self.mockmyclass = MockMyClass()
self.mocked = mock.patch(
'path-to-my-file.MyClass',
side_effect=self.mockmyclass.factory,
)
self.addCleanup(self.mocked.stop)
self.mocked = self.mocked.start()
def test_something(self):
# call code under test
# then test with
with self.mockmyclass['name-of-instance'] as inst:
inst.start.assert_called_once()
inst.stop.assert_called_once()
# or test with
self.mockmyclass['name-of-instance'].start.assert_called_once()

Mock entire python class

I'm trying to make a simple test in python, but I'm not able to figure it out how to accomplish the mocking process.
This is the class and def code:
class FileRemoveOp(...)
#apply_defaults
def __init__(
self,
source_conn_keys,
source_conn_id='conn_default',
*args, **kwargs):
super(v4FileRemoveOperator, self).__init__(*args, **kwargs)
self.source_conn_keys = source_conn_keys
self.source_conn_id = source_conn_id
def execute (self, context)
source_conn = Connection(conn_id)
try:
for source_conn_key in self.source_keys:
if not source_conn.check_for_key(source_conn_key):
logging.info("The source key does not exist")
source_conn.remove_file(source_conn_key,'')
finally:
logging.info("Remove operation successful.")
And this is my test for the execute function:
#mock.patch('main.Connection')
def test_remove_execute(self,MockConn):
mock_coon = MockConn.return_value
mock_coon.value = #I'm not sure what to put here#
remove_operator = FileRemoveOp(...)
remove_operator.execute(self)
Since the execute method try to make a connection, I need to mock that, I don't want to make a real connection, just return something mock. How can I make that? I'm used to do testing in Java but I never did on python..
First it is very important to understand that you always need to Mock where it the thing you are trying to mock out is used as stated in the unittest.mock documentation.
The basic principle is that you patch where an object is looked up,
which is not necessarily the same place as where it is defined.
Next what you would need to do is to return a MagicMock instance as return_value of the patched object. So to summarize this you would need to use the following sequence.
Patch Object
prepare MagicMock to be used
return the MagicMock we've just created as return_value
Here a quick example of a project.
connection.py (Class we would like to Mock)
class Connection(object):
def execute(self):
return "Connection to server made"
file.py (Where the Class is used)
from project.connection import Connection
class FileRemoveOp(object):
def __init__(self, foo):
self.foo = foo
def execute(self):
conn = Connection()
result = conn.execute()
return result
tests/test_file.py
import unittest
from unittest.mock import patch, MagicMock
from project.file import FileRemoveOp
class TestFileRemoveOp(unittest.TestCase):
def setUp(self):
self.fileremoveop = FileRemoveOp('foobar')
#patch('project.file.Connection')
def test_execute(self, connection_mock):
# Create a new MagickMock instance which will be the
# `return_value` of our patched object
connection_instance = MagicMock()
connection_instance.execute.return_value = "testing"
# Return the above created `connection_instance`
connection_mock.return_value = connection_instance
result = self.fileremoveop.execute()
expected = "testing"
self.assertEqual(result, expected)
def test_not_mocked(self):
# No mocking involved will execute the `Connection.execute` method
result = self.fileremoveop.execute()
expected = "Connection to server made"
self.assertEqual(result, expected)
I found that this simple solution works in python3: you can substitute a whole class before it is being imported for the first time. Say I have to mock class 'Manager' from real.manager
class MockManager:
...
import real.manager
real.manager.Manager = MockManager
It is possible to do this substitution in init.py if there is no better place.
It may work in python2 too but I did not check.

Asserting that __init__ was called with right arguments

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)

How to use unittest.mock to mock arbitrary ConfigParser calls in a unit test

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)

Categories