0

If I have two files with the following contents:

test_helper.py:

class Installer:
    def __init__(self, foo, bar, version):
        # Init stuff
        raise Exception('If we're here, mock didn't work')

    def __enter__(self):
        return self

    def __exit__(self, type, value, tb):
        # cleanup
        pass

    def install(self):
        # Install stuff
        raise Exception('If we're here, mock didn't work')

And test.py:

import unittest
from mock import patch
from test_helper import Installer

class Deployer:
    def deploy(self):
        with Installer('foo', 'bar', 1) as installer:
            installer.install()

class DeployerTest(unittest.TestCase):
    @patch('tests.test_helper.Installer', autospec=True)
    def testInstaller(self, mock_installer):
        deployer = Deployer()
        deployer.deploy()
        mock_installer.assert_called_once_with('foo', 'bar', 1)

The code above doesn't test correctly. The mock is not applied properly:

  File "/Library/Python/2.7/site-packages/mock-1.3.0-py2.7.egg/mock/mock.py", line 947, in assert_called_once_with
    raise AssertionError(msg)
AssertionError: Expected 'Installer' to be called once. Called 0 times.

If I make the following changes in test.py:

  1. Change from test_helper import Installer to import test_helper, and
  2. Change with Installer('foo', 'bar', 1) as installer: to with test_helper.Installer('foo', 'bar', 1) as installer:

The code then works. Why does the mock only apply when I use the fully qualified name? Is it supposed to work in the partially-qualified case?

Srikanth
  • 973
  • 2
  • 9
  • 19

1 Answers1

0

You are testing your Deployer class inside test.py, which is calling Installer. This installer is what you want to mock. So, your decorator should be with respect to that.

I don't know where you are testing from exactly. But as an example, if you are running your test from the same level as test.py, then you can simply do this to your decorator and it should work:

import unittest

from dower import Installer
from mock import patch

class Deployer:
    def deploy(self):
        with Installer('foo', 'bar', 1) as installer:
            installer.install()

class DeployerTest(unittest.TestCase):
    @patch('test.Installer', autospec=True)
    def testInstaller(self, mock_installer):
        deployer = Deployer()
        deployer.deploy()
        mock_installer.assert_called_once_with('foo', 'bar', 1)


if __name__ == '__main__':
    unittest.main()

Note: You should not be mocking within the Installer module. You don't care about Installer in this case. Just that it returns your Mock, so you can continue testing the behaviour of your Deployer. Think of it that way, and you will realize why you have to mock with respect to what you are testing.

idjaw
  • 25,487
  • 7
  • 64
  • 83
  • I guess this was a mix of issues. However, it was mainly to do with http://stackoverflow.com/questions/16060724/patch-why-wont-the-relative-patch-target-name-work That was what I was looking for. Thanks for the answer, though! – Srikanth Mar 01 '16 at 02:26
  • 1
    @Srikanth Read deeply https://docs.python.org/3/library/unittest.mock.html#where-to-patch (3 or 4 times if you need) then accept this answer because is the correct one. – Michele d'Amico Mar 01 '16 at 12:35