[Tutor] Mocking with "mock" in unit testing

eryksun eryksun at gmail.com
Fri Jan 17 11:50:39 CET 2014


On Fri, Jan 17, 2014 at 4:58 AM, James Chapman <james at uplinkzero.com> wrote:
> import mock
> import unittest
> import pinger
>
> class Test_Pinger(unittest.TestCase):
>
>     def test_ping_host_succeeds(self):
>         pinger = pinger.Pinger()

Are you using CPython? That raises an UnboundLocalError. Take a look
at the CPython bytecode:

    def test(self):
        pinger = pinger.Pinger()

    >>> dis.dis(test)
      2           0 LOAD_FAST                1 (pinger)
                  3 LOAD_ATTR                0 (Pinger)
                  6 CALL_FUNCTION            0
                  9 STORE_FAST               1 (pinger)
                 12 LOAD_CONST               0 (None)
                 15 RETURN_VALUE

Notice LOAD_FAST(1), where fast local 1 is the local variable
`pinger`. The value isn't assigned yet, and LOAD_FAST doesn't search
globals and builtins. You need a unique name for the instance, such as
`test_pinger`. Then the compiler knows to use LOAD_GLOBAL:

    def test(self):
        test_pinger = pinger.Pinger()

    >>> dis.dis(test)
      2           0 LOAD_GLOBAL              0 (pinger)
                  3 LOAD_ATTR                1 (Pinger)
                  6 CALL_FUNCTION            0
                  9 STORE_FAST               1 (test_pinger)
                 12 LOAD_CONST               0 (None)
                 15 RETURN_VALUE

Here's a version using a decorator instead:

    @mock.patch('pinger.subprocess')
    def test_ping_host_succeeds(self, subprocess):
        subprocess.Popen.return_value.returncode = 0
        test_pinger = pinger.Pinger()
        test_pinger.ping_host('localhost')
        subprocess.Popen.assert_called_once_with(['ping','localhost'],
                                                 shell=True)


More information about the Tutor mailing list