mocking injected class method - python

I have a Gateway class that has contains an instance of a Resource class. I've already done my unit testing on Resource, and to simplify testing, Resource is injected into Gateway as a dependency at initialization:
class Gateway:
def __init__(self, resource):
self._resource = resource(Master)
def list_things(self):
return self._resource.list_resource()
Now I'd like write unit test for Gateway to verify that resource.list_resource() gets called as a result of calling gateway.list_things(). My best attempt doesn't work:
class TestGateway(unittest.TestCase):
def test_list_things(self):
mock_resource = Mock()
g = modbus.gateway.Gateway(mock_resource)
g.list_things()
mock_resource.list_resource.assert_called_once()
The result:
AssertionError: Expected 'list_resource' to have been called once. Called 0 times.
What am I missing?

In the actual use case of the mock_resource that you passed into Gateway for your test case, the constructor in Gateway actually makes a further call to the resource argument as a constructor, so that what the test expects is actually emulating the checking of the call against the class method, not the instance method of the mock_resource. Demonstrating this using just the minimum number of statements can be done using the following:
>>> mock_resource = Mock()
>>> self_resource = mock_resource('Master') # emulate Gateway.__init__
>>> self_resource.list_resource() # emulate Gateway.list_things
<Mock name='mock().list_resource()' id='140441464498496'>
>>> mock_resource.list_resource.assert_called_once() # test_list_things
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.6/unittest/mock.py", line 795, in assert_called_once
raise AssertionError(msg)
AssertionError: Expected 'list_resource' to have been called once. Called 0 times.
>>> self_resource.list_resource.assert_called_once() # test the _actual_ call
>>>
Note that I had assigned self_resource to emulate the self._resource = resource(Master) statement in the constructor for Resource, and the mismatching of the test vs. what is actually executed should now be apparent.
To fix this, the test should check the call like so:
def test_list_things(self):
mock_resource = Mock()
g = modbus.gateway.Gateway(mock_resource)
g.list_things()
# mock_resource.list_resource.assert_called_once()
g._resource.list_resource.assert_called_once()

Related

Why I got TypeError if calling staticmethod without class-body?

Why it happens? is this "binding behavior”?
using staticmethod in class body
>>> class Test:
#staticmethod
def test(msg="asd"):
print(msg)
>>> test = Test()
>>> test.test()
asd
but when I using it without, got error:
>>> #staticmethod
def test(msg=""):
print(msg)
>>> test
<staticmethod object at 0x10dde9be0>
>>> test()
Traceback (most recent call last):
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydevd_bundle/pydevd_exec2.py", line 3, in Exec
exec(exp, global_vars, local_vars)
File "<input>", line 1, in <module>
TypeError: 'staticmethod' object is not callable
A static method is a method that is bound to a class but does not require the class instance to function.
For example, you may have a class built around a file type to retrieve raw data from a source file and pass it to a lexer, your constructor could have you pass in the raw file however you may want another function to open the file, validate it and retrieve the data. In this case, you could use a static function within the class as the function is related to the class but doesn't require the class to function (in this case it would return the actual class).
A code example:
class PythonFile:
def __init__(self, raw_data):
self._raw_data = raw_data
self._lexer = None
#staticmethod
def open(fp):
with open(fp, "r") as rFile:
data = rFile.read()
# validate, etc...
return PythonFile(data)
def lex(self):
self._lexer = PythonLex(self._raw_data)
return self._lexer.get_master_block_array()
# etc, etc...
python_file = PythonFile.open("some\\path.py")
print(python_file.lex())
ItzTheDodo.
Use a standalone function.
def test(msg=""):
print(msg)

Subclassing TestCase in Python: Overwriting Field in Parent TestCase

I'm writing integration tests for an Alexa app.
Our application uses a controller-request-response pattern. The controller receives a request with a specified intent and session variables, routes the request to functions that do some computation with the session variables, and returns a response object with the results of that computation.
We get the right behavior from UnhandledIntentTestCase as far as test_for_smoke is concerned. However, test_returning_reprompt_text
never fires, because returns_reprompt_text is never overwritten.
Can someone explain how I can overwrite it in the parent class and/or
how the correct intent name is passed to the request object in setUpClass?
intent_base_case.py
import unittest
import mycity.intents.intent_constants as intent_constants
import mycity.mycity_controller as mcc
import mycity.mycity_request_data_model as req
import mycity.test.test_constants as test_constants
###############################################################################
# TestCase parent class for all intent TestCases, which are integration tests #
# to see if any changes in codebase have broken response-request model. #
# #
# NOTE: Assumes that address has already been set. #
###############################################################################
class IntentBaseCase(unittest.TestCase):
__test__ = False
intent_to_test = None
returns_reprompt_text = False
#classmethod
def setUpClass(cls):
cls.controller = mcc.MyCityController()
cls.request = req.MyCityRequestDataModel()
key = intent_constants.CURRENT_ADDRESS_KEY
cls.request._session_attributes[key] = "46 Everdean St"
cls.request.intent_name = cls.intent_to_test
cls.response = cls.controller.on_intent(cls.request)
#classmethod
def tearDownClass(cls):
cls.controller = None
cls.request = None
def test_for_smoke(self):
self.assertNotIn("Uh oh", self.response.output_speech)
self.assertNotIn("Error", self.response.output_speech)
def test_correct_intent_card_title(self):
self.assertEqual(self.intent_to_test, self.response.card_title)
#unittest.skipIf(not returns_reprompt_text,
"{} shouldn't return a reprompt text".format(intent_to_test))
def test_returning_reprompt_text(self):
self.assertIsNotNone(self.response.reprompt_text)
#unittest.skipIf(returns_reprompt_text,
"{} should return a reprompt text".format(intent_to_test))
def test_returning_no_reprompt_text(self):
self.assertIsNone(self.response.reprompt_text)
test_unhandled_intent.py
import mycity.test.intent_base_case as base_case
########################################
# TestCase class for unhandled intents #
########################################
class UnhandledIntentTestCase(base_case.IntentBaseCase):
__test__ = True
intent_to_test = "UnhandledIntent"
returns_reprompt_text = True
output
======================================================================
FAIL: test_correct_intent_card_title (mycity.test.test_unhandled_intent.UnhandledIntentTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/wdrew/projects/alexa_311/my_city/mycity/mycity/test/intent_base_case.py", line 44, in test_correct_intent_card_title
self.assertEqual(self.intent_to_test, self.response.card_title)
AssertionError: 'UnhandledIntent' != 'Unhandled intent'
- UnhandledIntent
? ^
+ Unhandled intent
? ^^
======================================================================
FAIL: test_returning_no_reprompt_text (mycity.test.test_unhandled_intent.UnhandledIntentTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/wdrew/projects/alexa_311/my_city/mycity/mycity/test/intent_base_case.py", line 56, in test_returning_no_reprompt_text
self.assertIsNone(self.response.reprompt_text)
AssertionError: 'So, what can I help you with today?' is not None
----------------------------------------------------------------------
This is because of execution order. The SkipIf decorators are executed once during the parsing of the IntentBaseCase class. They aren't re-executed for each class or for each call to the test function.
The decorator pattern for SkipIf is designed for use with fixed global variables such as versions of dependent modules, operating system or some other external resource who's availability can be calculated or known in the global context.
Skipping tests is also something that should be done for external reasons, not for internal ones such as the needs of a sub-class. A skip is still a kind of failing test which is indicated in the report so you can see your test suite isn't exercising the whole of the functional scope of the project.
You should redesign your base class structure so functions are only available to run if the sub-class and skip using Skip for this. My recommendation would be:
class IntentBaseCase(unittest.TestCase):
...
class RepromptBaseCase(IntentBaseCase):
def test_returning_reprompt_text(self):
self.assertIsNotNone(self.response.reprompt_text)
class NoRepromptBaseCase(IntentBaseCase):
def test_returning_no_reprompt_text(self):
self.assertIsNone(self.response.reprompt_text)
You should also consider moving the response portion out of the setUp and put it into a test_ function of it's own and change these test_returning functions into a simpler assertReprompt and assertNoReprompt functions. It's a good idea to set up the tests in setUp, but not a good idea to run the actual code there.

proper way python mock __init__() method that returns a fake class

Trying to mock out calls to pyazure library for django testing, but I can't figure out how to mock out the PyAzure class constructor so that it doesn't cause a TypeError. Is there a better way to approach mocking out an access library that generates a connection object?
Anything I've tried other than None generates a TypeError, which means I can't really even begin to test any of the PyAzure connection methods with actual return values. What is the best way to replace a working class with a fake class using mock?
Test Error:
======================================================================
ERROR: test_management_certificate_connect (azure_cloud.tests.ViewsTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/bschott/Source/django-nimbis/apps/azure_cloud/tests.py", line 107, in test_management_certificate_connect
self.cert1.connect()
File "/Users/bschott/Source/django-nimbis/apps/azure_cloud/models.py", line 242, in connect
subscription_id=self.subscription.subscription_id)
TypeError: __init__() should return None, not 'FakeAzure'
----------------------------------------------------------------------
tests.py:
class ViewsTest(TestCase):
def setUp(self):
...
self.cert1 = ManagementCertificate.objects.create(
name="cert1",
subscription=self.subscription1,
management_cert=File(open(__file__), "cert1.pem"),
owner=self.user1)
...
class FakeAzure(object):
""" testing class for azure """
def list_services(self):
return ['service1', 'service2', 'service3']
def list_storages(self):
return ['storage1', 'storage2', 'storage3']
#mock.patch.object(pyazure.PyAzure, '__init__')
def test_management_certificate_connect(self, mock_pyazure_init):
mock_pyazure_init.return_value = self.FakeAzure()
self.cert1.connect()
assert mock_pyazure_init.called
models.py
class ManagementCertificate(models.Model):
# support connection caching to azure
_cached_connection = None
def connect(self):
"""
Connect to the management interface using these credentials.
"""
if not self._cached_connection:
self._cached_connection = pyazure.PyAzure(
management_cert_path=self.management_cert.path,
subscription_id=self.subscription.subscription_id)
logging.debug(self._cached_connection)
return self._cached_connection
You seem to have a misconception about what __init__() does. Its purpose is to initialise an instance that was already created earlier. The first argument to __init__() is self, which is the instance, so you can see it was already allocated when __init__() is called.
There is a method __new__() that is called before __init__() to create the actual instance. I think it would be much easier, though, to replace the whole class by a mock class, instead of mocking single methods.

Polymorphism with callables in Python

I have an interface class called iResource, and a number of subclasses, each of which implement the "request" method. The request functions use socket I/O to other machines, so it makes sense to run them asynchronously, so those other machines can work in parallel.
The problem is that when I start a thread with iResource.request and give it a subclass as the first argument, it'll call the superclass method. If I try to start it with "type(a).request" and "a" as the first argument, I get "" for the value of type(a). Any ideas what that means and how to get the true type of the method? Can I formally declare an abstract method in Python somehow?
EDIT: Including code.
def getSocialResults(self, query=''):
#for a in self.types["social"]: print type(a)
tasks = [type(a).request for a in self.types["social"]]
argss = [(a, query, 0) for a in self.types["social"]]
grabbers = executeChainResults(tasks, argss)
return igrabber.cycleGrabber(grabbers)
"executeChainResults" takes a list "tasks" of callables and a list "argss" of args-tuples, and assumes each returns a list. It then executes each in a separate thread, and concatenates the lists of results. I can post that code if necessary, but I haven't had any problems with it so I'll leave it out for now.
The objects "a" are DEFINITELY not of type iResource, since it has a single constructor that just throws an exception. However, replacing "type(a).request" with "iResource.request" invokes the base class method. Furthermore, calling "self.types["social"][0].request" directly works fine, but the above code gives me: "type object 'instance' has no attribute 'request'".
Uncommenting the commented line prints <type 'instance'> several times.
You can just use the bound method object itself:
tasks = [a.request for a in self.types["social"]]
# ^^^^^^^^^
grabbers = executeChainResults(tasks, [(query, 0)] * len(tasks))
# ^^^^^^^^^^^^^^^^^^^^^^^^^
If you insist on calling your methods through the base class you could also do it like this:
from abc import ABCMeta
from functools import wraps
def virtualmethod(method):
method.__isabstractmethod__ = True
#wraps(method)
def wrapper(self, *args, **kwargs):
return getattr(self, method.__name__)(*args, **kwargs)
return wrapper
class IBase(object):
__metaclass__ = ABCMeta
#virtualmethod
def my_method(self, x, y):
pass
class AddImpl(IBase):
def my_method(self, x, y):
return x + y
class MulImpl(IBase):
def my_method(self, x, y):
return x * y
items = [AddImpl(), MulImpl()]
for each in items:
print IBase.my_method(each, 3, 4)
b = IBase() # <-- crash
Result:
7
12
Traceback (most recent call last):
File "testvirtual.py", line 30, in <module>
b = IBase()
TypeError: Can't instantiate abstract class IBase with abstract methods my_method
Python doesn't support interfaces as e.g. Java does. But with the abc module you can ensure that certain methods must be implemented in subclasses. Normally you would do this with the abc.abstractmethod() decorator, but you still could not call the subclasses method through the base class, like you intend. I had a similar question once and I had the idea of the virtualmethod() decorator. It's quite simple. It essentially does the same thing as abc.abstratmethod(), but also redirects the call to the subclasses method. The specifics of the abc module can be found in the docs and in PEP3119.
BTW: I assume you're using Python >= 2.6.
The reference to "<type "instance" >" you get when you are using an "old style class" in Python - i.e.: classes not derived from the "object" type hierarchy. Old style classes are not supposed to work with several of the newer features of the language, including descriptors and others. AND, among other things, - you can't retrieve an attribute (or method) from the class of an old style class using what you are doing:
>>> class C(object):
... def c(self): pass
...
>>> type (c)
<class '__main__.C'>
>>> c = C()
>>> type(c).c
<unbound method C.c>
>>> class D: #not inheriting from object: old style class
... def d(self): pass
...
>>> d = D()
>>> type(d).d
>>> type(d)
<type 'instance'>
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'instance' has no attribute 'd'
>>>
Therefore, just make your base class inherit from "object" instead of "nothing" and check if you still get the error message when requesting the "request" method from type(a) :
As for your other observation:
"The problem is that when I start a thread with iResource.request and give it a subclass as the first argument, it'll call the superclass method."
It seems that the "right" thing for it to do is exactly that:
>>> class A(object):
... def b(self):
... print "super"
...
>>> class B(A):
... def b(self):
... print "child"
...
>>> b = B()
>>> A.b(b)
super
>>>
Here, I call a method in the class "A" giving it an specialized instance of "A" - the method is still the one in class "A".

Using methods of a class from another in python

I'm working through 'Dive Into Python' on Google App Engine and came across this error while attempting to call one class's methods from another:
ERROR __init__.py:463] create() takes exactly 1 argument (2 given)
Traceback (most recent call last):
File "main.py", line 35, in get
dal.create("sample-data");
File "dataAccess/dal.py", line 27, in create
self.data_store.create(data_dictionary);
TypeError: create() takes exactly 1 argument (2 given)
Here's my main class:
# filename: main.py
from dataAccess.dal import DataAccess
class MySampleRequestHandler(webapp.RequestHandler):
"""Configured to be invoked for a specific GET request"""
def get(self):
dal = DataAccess();
dal.create("sample-data"); # problem area
MySampleRequestHandler.get() tries to instantiate and invoke DataAccess which is defined else where:
# filename: dal.py
from dataAccess.datastore import StandardDataStore
class DataAccess:
"""Class responsible for wrapping the specific data store"""
def __init__(self):
self.data_store = None;
data_store_setting = config.SETTINGS['data_store_name'];
if data_store_setting == DataStoreTypes.SOME_CONFIG:
self.data_store = StandardDataStore();
logging.info("DataAccess init completed.");
def create(self, data_dictionary):
# Trying to access the data_store attribute declared in __init__
data_store.create(data_dictionary);
I thought I could call DataAccess.create() with 1 parameter for its argument, especially according to how Dive into Python notes about class method calls:
When defining your class methods, you must explicitly list self as the first
argument for each method, including __init__. When you call a method of an
ancestor class from within your class, you must include the self argument.
But when you call your class method from outside, you do not specify anything
for the self argument; you skip it entirely, and Python automatically adds the
instance reference for you.
In self.data_store.create(data_dictionary), the self.data_store refers to the object created by self.data_store = StandardDataStore() in the __init__ method.
It looks like the create method of a StandardDataStore object doesn't expect an additional argument.
It should be self.data_store.create(data_dictionary);

Categories