[Tutor] Mocking with "mock" in unit testing

eryksun eryksun at gmail.com
Fri Jan 17 02:05:10 CET 2014


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)


More information about the Tutor mailing list