[Tutor] Mocking with "mock" in unit testing

James Chapman james at uplinkzero.com
Fri Jan 17 10:58:06 CET 2014


Thanks eryksun.

There's a list for testing in python which I also posed the question to and
got pretty much the same answer as you provided.

The line if proc.returncode != 1 was a mistake. That 1 should have been a
zero.

As this question was just about mock and not really dealing with the bad
return code or exception handling or raising my final working example looks
like this:


pinger.py

----------------------------

import subprocess
 class Pinger(object):

    def ping_host(self, host_to_ping):
        cmd_string = 'ping %s' % (host_to_ping)
        cmd_args = cmd_string.split()
        proc = subprocess.Popen(cmd_args, shell=True)
        proc.wait()
        if proc.returncode != 0:
            raise Exception('Error code was: %d' % (proc.returncode))

 if __name__ == '__main__':
    PINGER = Pinger()
    PINGER.ping_host('localhost')

----------------------------



test_pinger.py

----------------------------

import mockimport unittestimport pinger
 class Test_Pinger(unittest.TestCase):

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

    def test_ping_host_fails_and_throws_exception(self):
        pinger = pinger.Pinger()
        with mock.patch('pinger.subprocess') as subprocess:
            subprocess.Popen.return_value.returncode = 1
            self.assertRaises(Exception, pinger.ping_host, 'localhost')

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

----------------------------




--
James


On 17 January 2014 01:05, eryksun <eryksun at gmail.com> wrote:

> On Thu, Jan 16, 2014 at 5:32 AM, James Chapman <james at uplinkzero.com>
> wrote:
> >
> > In my unittest I don't want to run the ping command, (It might not be
> > available on the build system) I merely want to check that a call to
> > subprocess.Popen is made and that the parameters are what I expect?
>
> You can mock `Popen` where it's accessed.
>
>     @mock.patch('subprocess.Popen')
>     def test_ping_host(self, Popen):
>         Popen.return_value.returncode = 0
>         pinger = Pinger()
>         pinger.ping_host('127.0.0.1')
>         Popen.assert_called_once_with(['ping','127.0.0.1'], shell=True)
>
> If the tutor_q module imported `Popen` into its namespace, then you'd
> patch tutor_q.Popen.
>
> >     def ping_host(self, host_to_ping):
> >         cmd_string = 'ping %s' % (host_to_ping)
> >         cmd_args = cmd_string.split()
>
> Splitting on spaces doesn't work generally. Use `shlex.split`, or
> build the list manually.
>
> >         proc = subprocess.Popen(cmd_args, shell=True)
>
> Maybe you really need the shell to process your command, but generally
> there's no reason to run the shell just to have it execute the command
> and wait. Plus it opens the door to security exploits.
>
> >         proc.wait()
> >         if proc.returncode != 1:
> >             raise Exception('Error code was: %d' % (proc.returncode))
>
> A non-zero return code signals an error. When using `Popen` directly,
> you can be consistent with `check_call` and `check_output` if you
> raise a `CalledProcessError` in this case:
>
>     retcode = proc.wait()
>     if retcode:
>         raise subprocess.CalledProcessError(retcode, cmd_args)
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/tutor/attachments/20140117/bc5b3ec2/attachment-0001.html>


More information about the Tutor mailing list