Вымывание вызова подпроцесса в Python
Мне нужен метод (run_script
). В частности, я хочу проверить, что происходит вызов subprocess.Popen
. Было бы еще лучше проверить, что subprocess.Popen
вызывается с определенными параметрами. Однако, когда я запускаю тест, я получаю TypeError: 'tuple' object is not callable
.
Как я могу проверить свой метод, чтобы убедиться, что подпроцесс на самом деле вызывается с помощью mocks?
@mock.patch('subprocess.Popen')
def run_script(file_path):
process = subprocess.Popen(['myscript', -M, file_path], stdout=subprocess.PIPE)
output,err = process.communicate()
return process.returncode
def test_run_script(self, mock_subproc_popen):
mock_subproc_popen.return_value = mock.Mock(communicate=('ouput','error'), returncode=0)
am.account_manager("path")
self.assertTrue(mock_subproc_popen.called)
Ответы
Ответ 1
Мне кажется необычным, что вы используете декоратор патча над функцией run_script
, так как вы не передаете там макет.
Как насчет этого:
def run_script(file_path):
process = subprocess.Popen(['myscript', -M, file_path], stdout=subprocess.PIPE)
output,err = process.communicate()
return process.returncode
@mock.patch('subprocess.Popen')
def test_run_script(self, mock_subproc_popen):
process_mock = mock.Mock()
attrs = {'communicate.return_value': ('output', 'error')}
process_mock.configure_mock(**attrs)
mock_subproc_popen.return_value = process_mock
am.account_manager("path") # this calls run_script somewhere, is that right?
self.assertTrue(mock_subproc_popen.called)
Прямо сейчас, ваш издеваемый подпроцесс. По-видимому, возвращает кортеж, заставляя process.communicate() поднимать TypeError: 'tuple' object is not callable.
. Поэтому наиболее важно получить значение return_value только в mock_subproc_popen.
Ответ 2
Существует пакет testfixtures, который вы можете использовать.
Вот пример использования макета Popen:
from unittest import TestCase
from testfixtures.mock import call
from testfixtures import Replacer, ShouldRaise, compare
from testfixtures.popen import MockPopen, PopenBehaviour
class TestMyFunc(TestCase):
def setUp(self):
self.Popen = MockPopen()
self.r = Replacer()
self.r.replace(dotted_path, self.Popen)
self.addCleanup(self.r.restore)
def test_example(self):
# set up
self.Popen.set_command('svn ls -R foo', stdout=b'o', stderr=b'e')
# testing of results
compare(my_func(), b'o')
# testing calls were in the right order and with the correct parameters:
process = call.Popen(['svn', 'ls', '-R', 'foo'], stderr=PIPE, stdout=PIPE)
compare(Popen.all_calls, expected=[
process,
process.communicate()
])
def test_example_bad_returncode(self):
# set up
Popen.set_command('svn ls -R foo', stdout=b'o', stderr=b'e',
returncode=1)
# testing of error
with ShouldRaise(RuntimeError('something bad happened')):
my_func()