Mock patch is not working with class in __init__.py - python

I'm trying to use patch to return the a Mock from within a method. The basic structure is as follows:
MyCode.py
class MyClass:
def __init__(self, first_name, last_name):
self.first = first_name
self.last = last_name
def get_greeting(self):
return 'Hello {f} {l}'.format(f=self.first, l=self.last)
def get_new_greeting(first_name, last_name):
obj = MyClass(first_name, last_name)
return obj.get_greeting()
my_code_test.py
import unittest
from mock import Mock, patch
import my_code
class TestMyCode(unittest.TestCase):
def setUp(self):
pass
#patch('my_code.MyClass')
def test_get_greeting(self, MockClass):
instance = MockClass.return_value
mock_greeting = 'Hello Me'
instance.get_greeting.return_value = mock_greeting
greeting = my_code.get_new_greeting('john', 'doe')
self.assertEqual(greeting, mock_greeting)
if __name__ == '__main__':
unittest.main()
The code above works fine for me. However, when I apply the same pattern to the real code that I'm trying to test, the real object (not the mock one) gets returned in the method being tested. I can't see any differences. The only think that is a bit different is that the real class is defined in a init.py file. I'm not sure if this make a difference or not? Has any seen this before?
Note: the actual lib is twilio 3.3.5 and I'm using Python 2.6.5 and Django 1.3.1 and Mock 0.7.2

I figured it out. It had nothing to do with __init__.py file. It was (as usual) my fault! :)
Just for anyone that is every trying to use Mock and patch with Twilio and SMS in the future, here is the solution:
I was Mocking the class twilio.rest.TwilioRestClient But, things are chained together and I needed to call patch on the inner class called SmsMessage. So, for my unit test, this works well:
#patch('twilio.rest.resources.SmsMessages')
def test_send_msg_valid_args(self, MockClass):
instance = MockClass.return_value
instance.create.return_value = None
to_number = '+15555555555'
msg = 'Hello world'
send_sms(to_number, msg)
instance.create.assert_called_once_with(to=to_number, body=msg, from_=default_from_number)
note: send_sms is really the function that I'm trying to test. I just wanted to make sure that it was calling twilio as expected and supplying the default_from_number. The value default_from_number is defined in the settings file and not really important for this example.

Related

Python: is it possible to wrap "#patch(path)" for re-use? (unittest)

As the doc "Where to patch" says, we need to patch where an object is looked up, (not where it's defined); so I understand that it's not possible to - let's say - create a reusable patch for a particular path
Imagine you have several modules importing an object you'd like to mock
# file_a.py
from foo.goo.hoo import settings
# file_b.py
from foo.goo.hoo import settings
# file_c.py
from foo.goo.hoo import settings
I was wondering if there is a way to create a decorator such as:
#mock_settings
def test_whatever(self, settings_mock):
...
instead of this solution:
#patch("some_module.file_a.settings")
def test_whatever(self, settings_mock):
...
#patch("some_module.file_b.settings")
def test_whatever(self, settings_mock):
...
#patch("some_module.file_c.settings")
def test_whatever(self, settings_mock):
...
As mentioned in the question, to patch an object you have to patch its reference in the module to be tested (in case it is imported using from ...import).
To have it patched in several modules, you can patch all of these modules with the same mock, and use that mock. If you know in advance which modules you want to patch, you can just do this. If you don't know them in advance, you have to try to patch the object in all loaded modules -- this may get a bit more complicated.
I will show an example using pytest and a pytest fixture, as this is more compact; you could wrap that in a decorator for usage in unittest, but that will not change the basics. Consider we have a class that needs to be mocked in several modules:
class_to_mock.py
class ClassToMock:
def foo(self, msg):
return msg
module1.py
from class_to_mock import ClassToMock
def do_something():
inst = ClassToMock()
return inst.foo("module1")
module2.py
from class_to_mock import ClassToMock
def do_something_else():
inst = ClassToMock()
return inst.foo("module2")
You can now write a fixture that mocks the class in all of these modules at once (here using pytest-mock for simplicity):
#pytest.fixture
def mocked_class(mocker):
mocked = Mock()
for module in ('module1', 'module2'):
mocker.patch(module + '.ClassToMock', mocked)
yield mocked
This can be used to test both modules:
def test_module1(mocked_class):
mocked_class.return_value.foo.return_value = 'mocked!'
assert module1.do_something() == 'mocked!'
def test_module2(mocked_class):
mocked_class.return_value.foo.return_value = 'mocked!'
assert module2.do_something_else() == 'mocked!'
If you want a generic version that mocks the class in all loaded modules, you can replace the fixture with something like this:
#pytest.fixture
def mocked_class(mocker):
mocked = Mock()
for name, module in list(sys.modules.items()):
if not inspect.ismodule(module):
continue
for cls_name, cls in module.__dict__.items():
try: # need that as inspect may raise for some modules
if inspect.isclass(cls) and cls_name == "ClassToMock":
mocker.patch(name + ".ClassToMock", mocked)
except Exception:
continue
yield mocked
This will work for this specific example - to generalize this, it has to consider more object types, the class shall be configurable, and there may be some more issues - opposed to the more simple version where you just enumerate the modules you want to patch, which will always work.
You could do something similar in unittest.setUp by putting the mock in an instance variable, though that is less elegant, because you are also responsible for stopping the mocking:
class ModulesTest(unittest.TestCase):
def setUp(self):
self.mocked_class = Mock()
self.mocks = []
for module in ('module1', 'module2'):
mocked = mock.patch(module + '.ClassToMock', self.mocked_class)
self.mocks.append(mocked)
mocked.start()
def tearDown(self):
for mocked in self.mocks:
mocked.stop()
def test_module1(self):
self.mocked_class.return_value.foo.return_value = 'mocked!'
assert module1.do_something() == 'mocked!'
And you can also wrap this in a decorator, to answer your original question at least partially:
def mocked_class_to_mock(f):
#wraps(f)
def _mocked_class_to_mock(*args, **kwargs):
mocked_class = Mock()
mocks = []
for module in ('module1', 'module2'):
mocked = mock.patch(module + '.ClassToMock', mocked_class)
mocks.append(mocked)
mocked.start()
kwargs['mocked_class'] = mocked_class # use a keyword arg for simplicity
f(*args, **kwargs)
for mocked in mocks:
mocked.stop()
return _mocked_class_to_mock
...
#mocked_class_to_mock
def test_module3(self, mocked_class):
mocked_class.return_value.foo.return_value = 'mocked!'
assert module3.do_something() == 'mocked!'
Of course, you can do the same with the more generic version, if needed.
Also note that I skipped the simpler case where the object is imported using import .... In this case, you have to patch the original module. In the generic fixture, you probably want to add that case always.

Can I have an abstract base class for my tornado.testing.AsyncTestCase tests?

Is it possible to have base classes that define tests in tornado that are themselves not run as tests?
Let's say I have the following minimal example as a base class:
from tornado.testing import AsyncTestCase, gen_test
from tornado.httpclient import HTTPRequest
class AbstractTestCase(AsyncTestCase):
def set_parameters(self):
#Set some parameter value here: self.uri = ...
raise NotImplementedError
#gen_test
def test_common_functionality(self):
req = HTTPRequest(self.uri, method = "GET")
response = yield self.client.fetch(req, raise_error=False)
self.assertEqual(200, response.code)
Now, I would like to make several test cases that inherit from this, define their own value for self.uri...and have some specific tests of their own. Like this:
class ConcreteTestCase(AbstractTestCase):
def set_parameters(self):
self.uri = "www.stackoverflow.com"
#gen_test
def test_some_other_thing(self):
self.assertEqual(2, 1+1)
However, when I try to run this, the AbstractTestCase is also run on its own, giving an error (the NotImplementedError). This happens even when I only try to run the inheriting tests.
Is there any way around this issue, or do I have to duplicate the functionality in each test case?
One way to do this is with multiple inheritance. The abstract class doesn't need to extend AsyncTestCase as long as that class is in the inheritance hierarchy at runtime.
class AbstractTestCase(object):
def set_parameters(self):
#Set some parameter value here: self.uri = ...
raise NotImplementedError
#gen_test
def test_common_functionality(self):
req = HTTPRequest(self.uri, method = "GET")
response = yield self.client.fetch(req, raise_error=False)
self.assertEqual(200, response.code)
class ConcreteTestCase(AbstractTestCase, AsyncTestCase):
def set_parameters(self):
self.uri = "www.stackoverflow.com"
#gen_test
def test_some_other_thing(self):
self.assertEqual(2, 1+1)
This is admittedly a little weird and mypy type checking doesn't like it. But it's simple and it works, and I haven't found a mypy-friendly alternative that I like.
CLI
python3 -m tornado.testing ConcreteTestCase.ConcreteTestCase
testmain.py
#!/usr/bin/env python3
import unittest
from tornado.testing import main
def all():
cases = ['ConcreteTestCase.ConcreteTestCase']
return unittest.defaultTestLoader.loadTestsFromNames(cases)
main()
test/runtests.py is a good example.

Python mock a method when specific argument

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.

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.

Mocking modules from boto in python

I am trying to write a unit test for a class that looks like below.
import boto
class ToBeTested:
def location(self, eb):
return eb.create_storage_location()
def some_method(self):
eb = boto.beanstalk.connect_to_region(region, access_key, secret_key)
location(eb)
Is there a way to mock boto.beanstalk.connect_to_region return value and finally mocking create_storage_location? I am new to patch and mock in python, so I have no idea how could I go about doing that. Could someone please let me know if there is a way to do this?
Thanks much!
The idea is to patch connect_to_region() so that it returns a Mock object, then you can define whatever methods you want on the mock, example:
import unittest
from mock import patch, Mock
class MyTestCase(unittest.TestCase):
#patch('boto.beanstalk.connect_to_region')
def test_boto(self, connect_mock):
eb = Mock()
eb.create_storage_location.return_value = 'test'
connect_mock.return_value = eb
to_be_tested = ToBeTested()
# assertions
See also:
An Introduction to Mocking in Python
Hope that helps.

Categories