【问题标题】:Mock an instance method with a certain return value?模拟具有一定返回值的实例方法?
【发布时间】:2016-04-08 00:00:15
【问题描述】:

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

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

我正在尝试使用mockunittest.TestCase 测试方法中测试它对 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. 测试日志调用是否正在运行,并使用进程的输出和返回代码。

第二个很简单,我只是注入一个 MagicMock 作为记录器对象,但我遇到了第一个问题。

【问题讨论】:

  • 尝试在@mock.patch.object 行中设置return_value
  • 试过了,同样的错误。
  • 正如错误中所说,您在返回中捕获了两个值...因为您的 return_value 必须是一个元组,例如。 (0,0)
  • @NaftuliTzviKay 我设法让它工作,但我的解决方案不包括使用@mock.patch.object(subprocess.Popen, 'communicate', autospec=True)。我会发布它,看看它是否有帮助。我很想了解您的修补方法以及为什么要在测试模块中同时修补 subprocess.PopenPopen
  • @idjaw 如果可能,我想自动指定,以便我的对象的行为与“现实生活”中的 Popen 对象完全一样。

标签: python unit-testing python-mock


【解决方案1】:

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

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

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

<class 'unittest.mock.NonCallableMagicMock'>

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

为我升旗的部分在这里:

除了 return_value 和 side_effect 没有 在不可调用的模拟上的含义。

需要进一步调查才能确定这究竟意味着什么。考虑到这一点,也许您已经尝试过,对单元测试的以下更改会产生成功的模拟结果:

@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 创建我们的模拟对象。从那里开始,其他一切都与您所做的几乎一致。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-21
    • 1970-01-01
    • 1970-01-01
    • 2021-03-02
    相关资源
    最近更新 更多