如何断言生成器调用了模拟函数?

How to assert that a mock function is called with a generator?

我正在使用 mockpy.test 进行单元测试。

一个被测函数如下所示:

def convert(rows):
    rows = (preprocess(r) for r in rows)
    return batch_process(True, rows)

在测试中,我会模拟preprocessbatch_process。 对于 preprocess,我使用 side_effect 配置多个 return 值,每一行一个。

如何断言传递给 batch_process 的第二个参数是 preprocessed 行的生成器表达式?

考虑简化您的测试:您需要测试的是作为生成器遍历的第二个参数 when 包含 preprocess() 为每一行返回的值。

我只是勾画一下我最终会如何写它:

from mock import Mock, call

preprocess_sentinels = [Mock() for _ in range(5)]
mock_preprocess.side_effect = preprocess_sentinels
rows = [Mock() for _ in range(5)]

convert(rows)

mock_preprocess.assert_has_calls([call(r) for r in rows])
args, kwargs = mock_batch_process.call_args
assert args[0]
for v, e in zip(args[1], preprocess_sentinels):
    assert v == e

当然,这样您 不严格检查 它是一个生成器,但您检查 convert() 行为。确切地检查你是如何实现它的(通过生成器或通过列表)不是测试应该做的,它是一个实现细节而不是一种行为:你应该编写在这两种情况下都有效的最佳测试。

我找到了一种方法来检查这个,但它有点冗长,但在我的项目中对我有用

from mock import Mock, patch
from types import GeneratorType

def test():
    mock_batch_process = patch('module.calling.batch_process')
    mock_preprocess = patch('module.calling.preprocess')
    # our test data for rows
    rows = ...

    convert(rows)

    assert mock_batch_process.call_count == 1
    args, kwargs = mock_batch_process.call_args
    # get an arg that is expected to be a generator, second arg in this case
    preprocess_rows = args[1]
    # check that our call arg is a generator
    assert isinstance(preprocess_rows, GeneratorType)
    # check that it contains all expected values
    assert list(preprocess_rows) == [mock_preprocess(row) for row in rows]