<div dir="ltr">Thank you Patrick.<div><br></div><div>This does help as it gives me a working start point to go from. Prior to your message I was being caught out by the line:</div><div><br></div><div><span style="font-family:&#39;courier new&#39;,monospace;font-size:13px">subprocess.Popen.return_value.</span><span style="font-family:&#39;courier new&#39;,monospace;font-size:13px">returncode = 0</span><br>
</div><div><span style="font-family:&#39;courier new&#39;,monospace;font-size:13px"><br></span></div><div><span style="font-size:13px"><font face="arial, helvetica, sans-serif">so cheers for that.</font></span></div><div>
<span style="font-size:13px"><font face="arial, helvetica, sans-serif"><br></font></span></div><div><br></div><div class="gmail_extra"><div>--<br>James<br></div>
<div class="gmail_extra"><br></div><div class="gmail_extra"><br></div><br><br><div class="gmail_quote">On 16 January 2014 13:32, Patrick Smith <span dir="ltr">&lt;<a href="mailto:pjs482@gmail.com" target="_blank">pjs482@gmail.com</a>&gt;</span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="im">&gt; You&#39;re quite right, I&#39;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.<div>


<br></div></div><div>That&#39;s exactly what you need to do and what `mock.patch` will accomplish for you. Consider the following the test code:</div><div><br></div><div><div><div class="im"><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><div class="im"><div><font face="courier new, monospace">        with mock.patch(&quot;tutor_q.subprocess&quot;) as subprocess:</font></div>
</div><div><font face="courier new, monospace">            subprocess.Popen.return_value.returncode = 0</font></div>

<div><font face="courier new, monospace">            pinger.ping_host(&#39;localhost&#39;)</font></div><div><font face="courier new, monospace">            subprocess.Popen.assert_called_once_with([&#39;ping&#39;,&#39;localhost&#39;], shell=True)</font></div>


</div><div><font face="courier new, monospace"><br></font></div><div>The important line here is `with mock.patch(&quot;tutor_q.subprocess&quot;) as subprocess:`. `mock.patch` will create a mock object and replace `subprocess` in the `tutor_q` module with that mock object (and then re-insert the real `subprocess` when the block is exited). That mock object is then assigned to a variable named `subprocess` that you can use in your tests (note: it can be assigned to any name you want, but using the same name as what&#39;s being mocked out will make the tests more readable).</div>


<div><br></div><div>The line where you&#39;re setting the return value of the mock also needs to be changed. If you do `subprocess.Popen.return_value = 0`, then in the `ping_host` method, `proc` will be 0, since it&#39;s the result of calling `subprocess.Popen`. So, you won&#39;t be able to do things like `proc.wait()` or `proc.return_code` since those attributes don&#39;t exist on an integer.  Instead, you can set the `return_code` on `subprocess.Popen.return_value`, which is itself a Mock object.</div>


<div class="gmail_extra"><br>Hope this helps!</div><span class="HOEnZb"><font color="#888888"><div class="gmail_extra"><br></div><div class="gmail_extra">- Patrick</div></font></span><div><div class="h5"><div class="gmail_extra">
<br><div class="gmail_quote">On Thu, Jan 16, 2014 at 6:22 AM, James Chapman <span dir="ltr">&lt;<a href="mailto:james@uplinkzero.com" target="_blank">james@uplinkzero.com</a>&gt;</span> wrote:<br>

<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir="ltr">Thanks Ned and Tiemo (not sure your mail went to the list?). You&#39;re quite right, I&#39;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&#39;m still unsure how to do that?<div>



<br></div><div>The code I&#39;ve used in the question is a simplified version of what I&#39;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&#39;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><div><div>
<br><br><div class="gmail_quote">On 16 January 2014 11:04, Ned Batchelder <span dir="ltr">&lt;<a href="mailto:ned@nedbatchelder.com" target="_blank">ned@nedbatchelder.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">




  
    
  
  <div text="#000000" bgcolor="#FFFFFF"><div><div>
    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&#39;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 = &#39;ping %s&#39; % (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(&#39;Error code was: %d&#39; %
                    (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&#39;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&#39;t work and I
                  suspect it&#39;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([&#39;ping&#39;,&#39;localhost&#39;])</font></div>
                  <div><font face="courier new, monospace">       
                      pinger.ping_host(&#39;127.0.0.1&#39;)</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&#39;ve created a mock, and it&#39;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&#39;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>
      <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(&#39;127.0.0.1&#39;)</font></div>
    </div><div>
    <font face="courier new, monospace"><font face="courier new,
        monospace">        </font>subprocess.assert_called_once_with([&#39;ping&#39;,&#39;localhost&#39;])</font>
    <br>
    <br></div>
    I&#39;ve also moved the assert to after the code under test, since
    that&#39;s when the calls will be available to make assertions about. (I
    didn&#39;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&#39;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(&quot;tutor_q.subprocess&quot;) 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(&#39;127.0.0.1&#39;)</font></div><div>
    <font face="courier new, monospace"><font face="courier new,
        monospace">            </font>subprocess.assert_called_once_with([&#39;ping&#39;,&#39;localhost&#39;])</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 &quot;from subprocess import Popen&quot;,
    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>
      <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__
                      == &#39;__main__&#39;:</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" target="_blank">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></div></div>
<br>_______________________________________________<br>
testing-in-python mailing list<br>
<a href="mailto:testing-in-python@lists.idyll.org" target="_blank">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></div></div></div>
</blockquote></div><br></div></div>