mocking subprocess.Popen dependant on import style

3.1k views Asked by At

When attempting to mock Popen I can only get it to succeed if the importing of subprocess matches in both unit test code and main module code.

Given following module listdir.py:

from subprocess import Popen, PIPE

def listdir(dir):
    cmd = ['ls', dir]
    pc = Popen(cmd, stdout=PIPE, stderr=PIPE)
    out, err = pc.communicate()
    if pc.returncode != 0:
        raise Exception
    return out

and following unit test code test_listdir.py

import subprocess
import listdir
import mock

@mock.patch.object(subprocess, 'Popen', autospec=True)
def test_listdir(mock_popen):
    mock_popen.return_value.returncode = 0
    mock_popen.return_value.communicate.return_value = ("output", "Error")
    listdir.listdir("/fake_dir")

For some reason Popen is not being mocked, due to the import style being different between the two python modules, and running the test always raises an exception.

If I change listdir.py to import all of subproces e.g.

import subprocess

def listdir(dir):
    cmd = ['ls', dir]
    pc = subprocess.Popen(cmd, stdout=subprocess.PIPE, 
                          stderr=subprocess.PIPE)
    out, err = pc.communicate()
    if pc.returncode != 0:
        raise ListingErrorException
    return out

Then "output" is returned in the test.

Anyone care to shed some light on why, my preference would be to have from subprocess import Popen, Pipe in both modules, but I just can't get that to mock.

1

There are 1 answers

0
Mark Tozzi On BEST ANSWER

You need to patch the copy of Popen in listdir, not the one you just imported. So, instead of @mock.patch.object(subprocess, 'Popen', autospec=True), try @mock.patch.object(listdir, 'Popen', autospec=True)

See this doc for more info: http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch