首页 > 解决方案 > 为了单元测试的目的,如何修补 Python 的 raise(或 return)关键字?

问题描述

mock.patch 能够捕获的 Python 关键字的命名空间是什么?

当有人想打补丁打开或打印然后mock.patch("builtins.open")mock.patch("mymodule.open")可以使用,但这不起作用,例如,使用raise.

如果我有raise somemodule.SomeException声明,那么我可以很容易地测试是否引发了异常,但是我如何断言实际上已经调用了 raise 关键字。pytest喜欢(在和的帮助下pytest-mock):

def test_myfunction_calls_raise(self, mocker):
    mocked = mocker.patch("mymodule.raise")
    mymodule.myfunction()
    mocked.assert_called_once()

编辑:我有一个很好的答案,所以 mock.patch 不适用。除此之外,除了明显的解决方案(测试结果)之外,是否还有其他有意义的技术来测试代码中是否调用了Python 关键字 ( raise, , ...)?return

编辑 2:我想测试的实际代码(pynput 的 Listener stop() 在 Xfce 中不能正常工作,所以我必须引发异常来停止 Listener 线程):

import pynput
# ...

def stop(self):
    """Stops listener by raising an exception."""
    try:
        raise pynput.mouse.Listener.StopException
    except pynput.mouse.Listener.StopException:
        return False

编辑 3: 上面的方法使用以下两种测试方法进行了单元测试:

def test_stop_stops_listener(self, mocker):
    """StopException is raised if MagicMock has got StopException attribute."""
    mocked = mocker.patch("pynput.mouse.Listener")
    with pytest.raises(TypeError):  # catching classes that do not inherit from BaseException is not allowed
        base.BaseMouse().stop()
    assert hasattr(mocked, "StopException")

def test_stop_returns_False(self, mocker):
    assert base.BaseMouse().stop() is False

标签: pythonunit-testingmockingpytestpytest-mock

解决方案


您不能修补关键字。它们是 Python 语法的一部分。当 Python 解释器获取一段 Python 代码时,它会对其进行解析并分几步将其转换为某种内部表示(“字节码”)。然而,字节码与原始源代码不再有直接关系——关键字已经“消失”。

相反,模块和类属性以及它们在代码中的访问仍然可以在字节码中找到。打补丁基本上就是临时替换这些属性。

或者,换一种说法,您可以修补属性,但关键字不是属性。


推荐阅读