为什么在为模拟引发错误时同时具有 return 值和副作用?

Why have both a return-value and a side-effect when raising an error for a mock?

不久前,我在 Whosebug 上发现了以下解决方案模式,用于从模拟函数中引发错误(我再也找不到原来的 link,抱歉):

    def _mock_raises_error(my_mock, error_type, output):
        my_mock.return_value = mock.Mock()
        my_mock.side_effect  = error_type(output)

    # for example
    with mock.patch('mymodule.interface.function') as mock_function:
        _mock_raises_error(mock_function, Exception, 'Some error-message')

这符合预期,所以我一直使用这种模式。

最近我的一位同事问为什么这个定义既有 return_value 也有 side_effect 以及为什么需要这样。令我遗憾的是,我无法得出正确的答案(只是我从 Whosebug 复制了它,但这并不能解释为什么它是正确的)。

所以现在我的问题(以便将来能够解释)是为什么 side_effect 不够? return_value 添加了什么?

让我们从 unittest.mock 的文档开始。

side_effect: A function to be called whenever the Mock is called. See the side_effect attribute. Useful for raising exceptions or dynamically changing return values. The function is called with the same arguments as the mock, and unless it returns DEFAULT, the return value of this function is used as the return value.

Alternatively side_effect can be an exception class or instance. In this case the exception will be raised when the mock is called.

return_value: The value returned when the mock is called. By default this is a new Mock (created on first access). See the return_value attribute.

人们通常使用 return_value 或 side_effect 进行测试。根据我的经验,我经常使用 return_value 进行测试。这是我的标准情况,当我试图模拟某些功能(例如获取目录的内容)而不需要进行函数调用(例如 OS 和可能变化)。

我很少模拟 side_effect(通常是在我测试函数对异常的反应时)。 (例如,当我处理一个文件未找到的异常时。)

在这个测试目录中文件是否存在的函数示例中,我可能想要同时获得结果(比如空文件列表)和副作用(比如文件不-发现异常)。但是一般我会测试一个或另一个。

OP 的示例仅测试异常,因此副作用很关键(但 return 值不是)。如果您同时测试两者,则两者都需要。