Reputation: 2391
I'm trying to write a python unittest that does a subprocess call, so I'd like to mock that call.
I've already gone through these SO questions (to no avail):
benchmark.py
from subprocess import Popen, PIPE, STDOUT
def some_func():
with Popen(some_list, stdout=PIPE, stderr=STDOUT) as process:
stdout, stderr = process.communicate(timeout=timeout)
test.py
import mock
@mock.patch('benchmark.Popen.communicate')
@mock.patch('benchmark.Popen')
def test_some_func(self, mock_popen, mock_comm):
mock_popen.return_value = 0
mock_comm.return_value = ('output', 'error')
foo = benchmark.some_func()
When running the unittest I get:
stdout, stderr = process.communicate(timeout=timeout)
ValueError: not enough values to unpack (expected 2, got 0)
It looks like I'm not mocking the return value of communicate
correctly; what am I doing wrong?
I took the comments and suggested answers to solve things like this:
test.py
import mock
@mock.patch('benchmark.Popen')
def test_some_func(self, mock_popen):
process = mock_popen.return_value.__enter__.return_value
process.returncode = 0
process.communicate.return_value = (b'some output', b'some error')
foo = benchmark.some_func()
Upvotes: 2
Views: 3783
Reputation: 2624
As jonrsharpe has mentioned, with Popen(...) as process
is using Popen
instance as a context manager, which calls __enter__
method and assign its value to process
.
jonsharpe's solution uses return_value
magic of mock
and it works fine. But you can also implement a context manager and wrap the mocking logic in it:
import mock
import subprocess
class MockedPopen:
def __init__(self, args, **kwargs):
self.args = args
self.returncode = 0
def __enter__(self):
return self
def __exit__(self, exc_type, value, traceback):
pass
def communicate(self, input=None, timeout=None):
if self.args[0] == 'ls':
stdout = '\n'.join(['hello.txt', 'world.txt'])
stderr = ''
self.returncode = 0
else:
stdout = ''
stderr = 'unknown command'
self.returncode = 1
return stdout, stderr
@mock.patch('subprocess.Popen', MockedPopen)
def foo():
with subprocess.Popen(['ls']) as proc:
stdout, stderr = proc.communicate()
print(stdout, stderr)
foo()
Output:
hello.txt
world.txt
Upvotes: 2