在Python中模拟子进程调用

2024-04-25 09:41:32 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个方法(run_script)想要测试。具体来说,我想测试对subprocess.Popen的调用是否发生。最好测试使用某些参数调用subprocess.Popen。当我运行测试时,我得到TypeError: 'tuple' object is not callable

如何测试我的方法以确保实际使用mock调用子流程?

@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)

Tags: path方法runreturndefscriptprocessmock
2条回答

在我看来,在run_script函数上使用patch decorator是不寻常的,因为您没有在那里传递模拟参数。

这个怎么样:

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)

现在,模拟的subprocess.Popen似乎返回一个元组,使process.communicate()引发TypeError: 'tuple' object is not callable.。因此,在mock_subpc_popen上获得正确的返回值是最重要的。

存在可以使用的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()

相关问题 更多 >