可以在不模拟对象的其他属性的情况下模拟 python 构造函数吗?

ton*_*oft 5 python unit-testing mocking python-mock

是否可以在继续使用同名其他字段/函数的生产版本的同时模拟 python 构造函数?例如,给定生产代码:

class MyClass:
    class SubClass:
        def __init__(self) -> None:
            print("\nreal sub init called")

        class SubSubClass:
            def __init__(self) -> None:
                print("\nreal sub sub init called")
Run Code Online (Sandbox Code Playgroud)

以及以下测试代码:

class FakeSubClass:
    def __init__(self) -> None:
        print("\nfake init called")


def test():
    MyClass.SubClass()
    MyClass.SubClass.SubSubClass()

    MyClass.SubClass = Mock(side_effect=FakeSubClass)

    MyClass.SubClass()
    MyClass.SubClass.SubSubClass()
Run Code Online (Sandbox Code Playgroud)

我们得到以下输出:

real sub init called

real sub sub init called

fake init called
Run Code Online (Sandbox Code Playgroud)

请注意,最后一行MyClass.SubClass.SubSubClass()没有创建真正的 SubSubClass,因为此时它是 SubClass 模拟的自动创建的属性。

我想要的输出如下:

real sub init called

real sub sub init called

fake init called

real sub sub init called
Run Code Online (Sandbox Code Playgroud)

换句话说,我只想模拟子类,而不是子子类。我已经尝试过代替上面的嘲笑线的事情(两者都不起作用):

MyClass.SubClass.__init__ = Mock(side_effect=FakeSubClass.__init__)

MyClass.SubClass.__new__ = Mock(side_effect=FakeSubClass.__new__)
Run Code Online (Sandbox Code Playgroud)

请注意,我知道可以通过多种方式重构代码以避免此问题,但遗憾的是代码无法重构。

Zho*_*uan 5

您也可以伪造该类,MyClass.SubClass.SubSubClass()在您的情况下不起作用的是MyClass.SubClass模拟没有SubSubClass定义。只需让其FakeSubClass从 MyClass.SubClass 继承即可解决问题。

您可以轻松地修补MyClassFakeClass,并且您将拥有正确的测试对象而不是真实对象。

from unittest.mock import Mock


class MyClass:
    class SubClass:
        def __init__(self) -> None:
            print("\nreal sub init called")

        class SubSubClass:
            def __init__(self) -> None:
                print("\nreal sub sub init called")


class FakeSubClass(MyClass.SubClass, Mock):
    def __init__(self) -> None:
        print("\nfake init called")


class FakeClass:
    class SubClass(FakeSubClass):
        pass


def test():
    MyClass.SubClass()
    MyClass.SubClass.SubSubClass()

    MyClass.SubClass = Mock(side_effect=FakeSubClass)

    FakeClass.SubClass()
    FakeClass.SubClass.SubSubClass()
Run Code Online (Sandbox Code Playgroud)