不止一次嘲笑 subprocess.check_call

Mocking subprocess.check_call more than once

我有一个调用 subprocess.check_call() 两次的函数。我想测试所有可能的输出。我希望能够将第一个 check_call() 设置为 return 1,将第二个设置为 return 0,并对所有可能的组合都这样做。以下是我到目前为止所拥有的。我不确定如何调整预期的 return 值

@patch('subprocess.check_call')
def test_hdfs_dir_func(mock_check_call):
    for p, d in list(itertools.product([1, 0], repeat=2)):
        if p or d:

您可以将您的 mock 的 side_effect 分配给一个可迭代对象,这将在每次调用它时 return 迭代对象中的下一个值。在这种情况下,您可以这样做:

import copy
import itertools
import subprocess

from unittest.mock import patch

@patch('subprocess.check_call')
def test_hdfs_dir_func(mock_check_call):
    return_values = itertools.product([0, 1], repeat=2)
    # Flatten the list; only one return value per call
    mock_check_call.side_effect = itertools.chain.from_iterable(copy.copy(return_values))
    for p, d in return_values:
        assert p == subprocess.check_call()
        assert d == subprocess.check_call()

注意几点:

  • 我没有你的原始函数,所以我在循环中调用了自己对 check_call 的调用。
  • 我在原始 itertools.product return 值上使用副本,因为如果我不这样做,它将使用原始迭代器。当我们想要的是 2 个单独的列表时,这会耗尽原始迭代器:一个用于模拟的 side_effect,另一个用于您在测试中循环。
  • 您可以使用 side_effect 做其他巧妙的事情,而不仅仅是加注。如上所示,多次调用可以更改return值:https://docs.python.org/3/library/unittest.mock-examples.html#side-effect-functions-and-iterables
    • 不仅如此,从上面的link可以看出,还可以给它一个函数指针。这允许您在跟踪多个模拟调用时执行更复杂的逻辑。