gpt4 book ai didi

python - 如何恢复模拟

转载 作者:行者123 更新时间:2023-12-04 07:19:47 24 4
gpt4 key购买 nike

当我弄清楚为什么我的测试文件运行顺利但如果测试与 pytest 一起为整个包运行时中断,我几乎要发疯了。 .
我有一个父类和一个子类。 child 调用 parent 的 init 如下:

class Child(Parent):
__init__(...,**kwargs):
...
super().__init__(**kwargs)
我想通过一个小测试来定义这种行为。
from unittest.mock import Mock

def test_init_calls_parent_init():
Parent.__init__ = Mock()

Child()

assert Parent.__init__.called
问题是 Parent.__init__ 始终保持模拟对于其他文件中的所有以下测试。
我的想法是把它放到一个函数作用域中使它只是一个临时的改变。当然,由于这些测试失败了,它们隐含地定义了对父级 init 的需求,但我也想确保使用一个显式测试。
我应该创建一些 pytest设置/拆卸或防止这种情况的公认方法是什么?

最佳答案

当你执行 Parent.__init__ = Mock() ,你基本上重新定义了__init__模块本身,然后反射(reflect)在随后的测试中。
而不是手动更改 Parent.__init__ 的实现至Mock ,我的建议是改用 unittest 中已有的修补功能。和 pytest-mock .

  • 我们也可以使用monkeypatch正如@MattSom 所建议的(见评论部分)

  • src.py
    class Parent:
    def __init__(self, **kwargs):
    print("Parent __init__ called")

    class Child(Parent):
    def __init__(self, **kwargs):
    super().__init__(**kwargs)
    print("Child __init__ called")
    没有更正:
    from unittest.mock import Mock, patch

    from src import Child, Parent


    def test_init_calls_real_parent_init():
    Child()


    def test_init_calls_updated_parent_init():
    Parent.__init__ = Mock()

    Child()

    assert Parent.__init__.called


    def test_init_calls_real_parent_init_2():
    Child()
    输出:
    $ pytest -q test_src.py -rP
    ... [100%]
    ================================================================================================= PASSES ==================================================================================================
    ____________________________________________________________________________________ test_init_calls_real_parent_init _____________________________________________________________________________________
    ------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
    Parent __init__ called
    Child __init__ called
    ___________________________________________________________________________________ test_init_calls_updated_parent_init ___________________________________________________________________________________
    ------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
    Child __init__ called
    ___________________________________________________________________________________ test_init_calls_real_parent_init_2 ____________________________________________________________________________________
    ------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
    Child __init__ called
    3 passed in 0.01s
    发现:
    第一次测试称为真正的 Parent.__init__ .第二次测试称为模拟。然而在第三次测试中,它也意外地调用了第二次测试中的mock。
    有修正:
    from unittest.mock import Mock, patch

    from src import Child, Parent


    def test_init_calls_real_parent_init():
    Child()


    # Personally I wouldn't advise to do this. It just works :)
    def test_init_calls_updated_parent_init():
    # Setup
    orig_parent_init = Parent.__init__ # Store original init

    # Real test
    Parent.__init__ = Mock()

    Child()

    assert Parent.__init__.called

    # Teardown
    Parent.__init__ = orig_parent_init # Bring back the original init


    @patch("src.Parent.__init__") # Uses unittest
    def test_init_calls_mocked_parent_init(mock_parent_init):
    Child()

    assert mock_parent_init.called


    def test_init_calls_mocked_parent_init_2(mocker): # Uses pytest-mock
    mock_parent_init = mocker.patch("src.Parent.__init__")

    Child()

    assert mock_parent_init.called


    def test_init_calls_real_parent_init_2():
    Child()
    输出:
    $ pytest -q test_src_2.py -rP
    ..... [100%]
    ================================================================================================= PASSES ==================================================================================================
    ____________________________________________________________________________________ test_init_calls_real_parent_init _____________________________________________________________________________________
    ------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
    Parent __init__ called
    Child __init__ called
    ___________________________________________________________________________________ test_init_calls_updated_parent_init ___________________________________________________________________________________
    ------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
    Child __init__ called
    ___________________________________________________________________________________ test_init_calls_mocked_parent_init ____________________________________________________________________________________
    ------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
    Child __init__ called
    __________________________________________________________________________________ test_init_calls_mocked_parent_init_2 ___________________________________________________________________________________
    ------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
    Child __init__ called
    ___________________________________________________________________________________ test_init_calls_real_parent_init_2 ____________________________________________________________________________________
    ------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
    Parent __init__ called
    Child __init__ called
    5 passed in 0.03s
    发现:
    在这里,我使用了 2 个解决方案:
  • 要么改回 Parent.__init__ 的原始实现手动重新分配到模拟后(参见 test_init_calls_updated_parent_init )(不建议)
  • 或者使用 unittest 和 pytest-mock 的内置修补功能(参见 test_init_calls_mocked_parent_inittest_init_calls_mocked_parent_init_2 )

  • 现在,第一个和最后一个测试都正确调用了实际的 Parent.__init__ ,即使在所有的 mock 之后。

    关于python - 如何恢复模拟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68576703/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com