Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 419
bfschott Avatar asked Mar 12 '12 15:03

bfschott


People also ask

Can we mock a class in Python?

mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects and make assertions about how they have been used. unittest. mock provides a core Mock class removing the need to create a host of stubs throughout your test suite.

How do you write a case mock test in Python?

If you want to mock an object for the duration of your entire test function, you can use patch() as a function decorator. These functions are now in their own file, separate from their tests. Next, you'll re-create your tests in a file called tests.py .


1 Answers

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.

like image 117
Sven Marnach Avatar answered Sep 26 '22 02:09

Sven Marnach