Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mocking subprocess.Popen dependant on import style

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.

like image 975
Matt Keenan Avatar asked Jun 25 '15 12:06

Matt Keenan


1 Answers

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

like image 172
Mark Tozzi Avatar answered Nov 02 '22 12:11

Mark Tozzi