代码之家  ›  专栏  ›  技术社区  ›  Constantino

python单元测试模拟Popen和Popen.communicate

  •  0
  • Constantino  · 技术社区  · 7 年前

    我试图编写一个python单元测试来执行子流程调用,因此我想模拟该调用。

    我已经讨论了这些问题(没有用):

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

    运行unittest时,我得到:

        stdout, stderr  = process.communicate(timeout=timeout)
    ValueError: not enough values to unpack (expected 2, got 0)
    

    看起来我不是在嘲笑 communicate

    解决方案

    我采纳了这些意见并提出了解决问题的建议:

    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()
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   CtheSky    7 年前

    正如Jornsharpe提到的, with Popen(...) as process Popen 实例作为上下文管理器,它调用 __enter__ 方法并将其值指定给 process .

    jonsharpe的解决方案使用 return_value mock

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

    输出:

    hello.txt
    world.txt