[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