模拟具有特定返回值的实例方法?

时间:2016-04-08 00:00:15

标签: python unit-testing python-mock

我有一个测试方法,如下所示:

def execute_update(self):
    """Execute an update."""
    p = subprocess.Popen(['command'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

    try:
        stdout, stderr = p.communicate()

        if p.returncode == 0:
            # successful update, notify
            self.logger.info("Successfully updated.")
        else:
            # failed update, notify
            self.logger.error("Unable to update (code {returncode}):\n{output}".format(
                returncode=p.returncode, output=stdout))

    except KeyboardInterrupt as e:
        # terminate the process
        p.terminate()
        raise e

我正在尝试使用unittest.TestCasemock测试方法中测试其对Popen的调用及其对日志记录函数的调用:

@mock.patch.object(subprocess.Popen, 'communicate', autospec=True)
@mock.patch('updateservice.subprocess.Popen', autospec=True)
def test_fetch_metadata(self, mock_popen, mock_communicate):
    """Test that we can fetch metadata correctly."""
    mock_communicate.return_value = ("OUT", "ERR")
    mock_popen.returncode = 0

    self.reference.execute_update()

    # other asserts

最后一行失败了:

    stdout, stderr = p.communicate()
ValueError: need more than 0 values to unpack

我做错了什么?我有以下要求:

  1. 测试使用正确的值调用subprocess.Popen的构造函数。
  2. 使用输出的输出和返回代码测试日志记录调用是否正在运行。
  3. 第二个很容易,我只是注入一个MagicMock作为记录器对象,但我遇到了第一个问题。

1 个答案:

答案 0 :(得分:2)

我认为主要问题来自你的补丁对象:

@mock.patch.object(subprocess.Popen, 'communicate', autospec=True)

奇怪的是,似乎正在创建的模拟类型是:

<class 'unittest.mock.NonCallableMagicMock'>

这是我之前第一次遇到NonCallableMagicMock类型,但查看我在此找到的最少信息,文档指定this

为我举起旗帜的部分在这里:

  

除了return_value和side_effect之外没有   意思是不可嘲笑的模拟。

需要进一步调查才能确定具体含义。考虑到这一点,也许你已经尝试过了,对你的unittest进行以下更改会产生成功的模拟结果:

@mock.patch('server.upd.subprocess.Popen', autospec=True)
def test_fetch_metadata(self, mock_popen):
    """Test that we can fetch metadata correctly."""

    mock_popen.return_value = Mock()
    mock_popen_obj = mock_popen.return_value

    mock_popen_obj.communicate.return_value = ("OUT", "ERR")
    mock_popen_obj.returncode = 0

    self.reference.execute_update()

因此,正如您所看到的,我们几乎按照mock_popen.return_value创建了我们的模拟对象。从那里开始,其他一切都与你正在做的事情完全一致。