<div dir="ltr">Thanks Ned and Tiemo (not sure your mail went to the list?). You're quite right, I've not mocked the subprocess class which thinking about it is really what I want to do and somehow inject that into the test so that when I call Pinger it uses the mock subprocess class rather than the real one. Although as of writing I'm still unsure how to do that?<div>
<br></div><div>The code I've used in the question is a simplified version of what I'm actually doing. ping was substituted for a tool that will not be available on a continuous integration build system. If I can understand and get a simplified mock to work, then I'll hopefully be able to use my new found knowledge to mock out the real test.</div>
<div class="gmail_extra"><br clear="all"><div>--<br>James<br></div>
<br><br><div class="gmail_quote">On 16 January 2014 11:04, Ned Batchelder <span dir="ltr"><<a href="mailto:ned@nedbatchelder.com" target="_blank">ned@nedbatchelder.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div text="#000000" bgcolor="#FFFFFF"><div><div class="h5">
On 1/16/14 5:40 AM, James Chapman wrote:<br>
<blockquote type="cite">
<div dir="ltr">
<div class="gmail_quote">Originally sent to python tutor mailing
list but I suspect this might be a better list for my
question...<br>
<br>
<div dir="ltr">
<div>Hi all<br>
</div>
<div dir="ltr">
<div><br>
</div>
<div>
I have a question regarding mocking in unit testing.</div>
<div><br>
</div>
<div>Let's assume I have the following class:</div>
<div><br>
</div>
<div><font face="courier new, monospace">-------------------------------------------</font></div>
<div><font face="courier new, monospace">import subprocess<br>
</font></div>
<div><font face="courier new, monospace"><br>
</font></div>
<div>
<div><font face="courier new, monospace">class
Pinger(object):</font></div>
<div><font face="courier new, monospace"><br>
</font></div>
<div><font face="courier new, monospace"> def
ping_host(self, host_to_ping):</font></div>
<div><font face="courier new, monospace">
cmd_string = 'ping %s' % (host_to_ping)</font></div>
<div><font face="courier new, monospace">
cmd_args = cmd_string.split()</font></div>
<div><font face="courier new, monospace"> proc =
subprocess.Popen(cmd_args, shell=True)</font></div>
<div><font face="courier new, monospace">
proc.wait()</font></div>
<div><font face="courier new, monospace"> if
proc.returncode != 1:</font></div>
<div><font face="courier new, monospace">
raise Exception('Error code was: %d' %
(proc.returncode))</font></div>
</div>
<div>
<div><font face="courier new, monospace"><br>
</font></div>
<div><font face="courier new, monospace">-------------------------------------------</font></div>
<div><br>
</div>
<div><br>
</div>
<div>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?</div>
<div><br>
</div>
<div>So far I have this, but it doesn't work and I
suspect it's way off!!</div>
<div><br>
</div>
<div><br>
</div>
<div><font face="courier new, monospace">-------------------------------------------<br>
</font></div>
<div>
<div><font face="courier new, monospace">import mock</font></div>
<div><font face="courier new, monospace">import
unittest</font></div>
<div><font face="courier new, monospace">from tutor_q
import Pinger</font></div>
<div>
<font face="courier new, monospace"><br>
</font></div>
<div><font face="courier new, monospace">class
Test_Pinger(unittest.TestCase):</font></div>
<div><font face="courier new, monospace"><br>
</font></div>
<div><font face="courier new, monospace"> def
test_ping_host(self):</font></div>
<div><font face="courier new, monospace">
pinger = Pinger()</font></div>
<div><font face="courier new, monospace">
assert pinger</font></div>
<div><font face="courier new, monospace">
subprocess = mock.Mock()</font></div>
<div><font face="courier new, monospace">
subprocess.Popen.return_value = 0</font></div>
<div><font face="courier new, monospace">
subprocess.assert_called_once_with(['ping','localhost'])</font></div>
<div><font face="courier new, monospace">
pinger.ping_host('127.0.0.1')</font></div>
<div><font face="courier new, monospace"><br>
</font></div>
</div>
</div>
</div>
</div>
</div>
</div>
</blockquote></div></div>
Often the trickiest thing about mocking is mocking the right name.
Here you've created a mock, and it's assigned to a local variable
named subprocess. This name is not related to the global subprocess
name in the tutor_q module, which is the name that will be used by
your pinger object.<br>
<br>
You have to assign to that global. A module's globals are also
available as attributes on the module object, so you can do it like
this:<br>
<br>
<font face="courier new, monospace"><br>
</font>
<div>
<div><font face="courier new, monospace">import mock</font></div>
<div><font face="courier new, monospace">import unittest</font></div>
<div><font face="courier new, monospace">import tutor_q<br>
</font></div>
<div>
<font face="courier new, monospace"><br>
</font></div>
<div><font face="courier new, monospace">class
Test_Pinger(unittest.TestCase):</font></div>
<div><font face="courier new, monospace"><br>
</font></div>
<div><font face="courier new, monospace"> def
test_ping_host(self):</font></div>
<div><font face="courier new, monospace"> pinger =
tutor_q.Pinger()</font></div><div class="im">
<div><font face="courier new, monospace"> assert pinger</font></div>
<div><font face="courier new, monospace"> subprocess =
mock.Mock()</font></div>
<div><font face="courier new, monospace">
subprocess.Popen.return_value = 0</font></div>
</div><div><font face="courier new, monospace">
tutor_q.subprocess = subprocess</font><font face="courier new,
monospace"><br>
pinger.ping_host('127.0.0.1')</font></div>
</div><div class="im">
<font face="courier new, monospace"><font face="courier new,
monospace"> </font>subprocess.assert_called_once_with(['ping','localhost'])</font>
<br>
<br></div>
I've also moved the assert to after the code under test, since
that's when the calls will be available to make assertions about. (I
didn't actually test this code, if I got it wrong, someone on the
list will school me!)<br>
<br>
A problem with this code is that you replace tutor_q.subprocess with
a mock, but you don't restore it when the test is done. Mock has
tools that can do this for you:<br>
<br>
<div><font face="courier new, monospace"><br>
</font></div>
<div><font face="courier new, monospace"> def
test_ping_host(self):</font></div>
<div><font face="courier new, monospace"> pinger =
tutor_q.Pinger()</font></div>
<div><font face="courier new, monospace"> assert pinger</font></div>
<div><font face="courier new, monospace"> with
mock.patch("tutor_q.subprocess") as subprocess:<br>
subprocess = mock.Mock()</font></div>
<div><font face="courier new, monospace">
subprocess.Popen.return_value = 0</font></div>
<div><font face="courier new, monospace"> </font><font face="courier new, monospace">pinger.ping_host('127.0.0.1')</font></div><div class="im">
<font face="courier new, monospace"><font face="courier new,
monospace"> </font>subprocess.assert_called_once_with(['ping','localhost'])</font>
<br>
<br></div>
Note that there is an unfortunate coupling of your test code and
product code: the test had to know how subprocess was imported. In
tutor_q.py, if you had instead used "from subprocess import Popen",
then your mock would have to be similarly adjusted to mock
tutor_q.Popen instead of tutor_q.subprocess.<br>
<br>
--Ned.<br>
<blockquote type="cite"><div class="im">
<div dir="ltr">
<div class="gmail_quote">
<div dir="ltr">
<div dir="ltr">
<div>
<div>
<div>
<font face="courier new, monospace"><br>
</font></div>
<div><font face="courier new, monospace"><br>
</font></div>
<div><font face="courier new, monospace">if __name__
== '__main__':</font></div>
<div><font face="courier new, monospace">
unittest.main()</font></div>
</div>
<div><font face="courier new, monospace"><br>
</font></div>
<div><font face="courier new, monospace">-------------------------------------------</font><br>
</div>
<div><br>
</div>
<div><br>
</div>
<div>Can anyone point me in the right direction on how
to mock up these subprocess calls?</div>
<div><br>
</div>
<div>Thanks</div>
<span><font color="#888888">
<div><br>
</div>
<div>James</div>
<div><br>
</div>
<div><br>
</div>
<div><br>
</div>
</font></span></div>
</div>
</div>
</div>
<br>
</div>
<br>
<fieldset></fieldset>
<br>
</div><pre>_______________________________________________
testing-in-python mailing list
<a href="mailto:testing-in-python@lists.idyll.org" target="_blank">testing-in-python@lists.idyll.org</a>
<a href="http://lists.idyll.org/listinfo/testing-in-python" target="_blank">http://lists.idyll.org/listinfo/testing-in-python</a>
</pre>
</blockquote>
<br>
</div>
<br>_______________________________________________<br>
testing-in-python mailing list<br>
<a href="mailto:testing-in-python@lists.idyll.org">testing-in-python@lists.idyll.org</a><br>
<a href="http://lists.idyll.org/listinfo/testing-in-python" target="_blank">http://lists.idyll.org/listinfo/testing-in-python</a><br>
<br></blockquote></div><br></div></div>