簡體   English   中英

可以在不模仿對象的其他屬性的情況下模擬python構造函數嗎?

[英]Can a python constructor be mocked without mocking other properties of the object?

是否有可能在繼續使用同名的其他字段/函數的生產版本時模擬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")

以及以下測試代碼:

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()

我們得到以下輸出:

real sub init called

real sub sub init called

fake init called

請注意,最后一行MyClass.SubClass.SubSubClass()沒有創建真正的SubSubClass,因為此時它是SubClass mock的自動創建的屬性。

我想要的輸出如下:

real sub init called

real sub sub init called

fake init called

real sub sub init called

換句話說,我想只模擬SubClass,而不是SubSubClass。 我嘗試過的東西代替上面的模擬線(兩者都不起作用):

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

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

請注意,我知道可以通過幾種方法重構代碼以避免此問題,但遺憾的是代碼無法重構。

你也可以偽造這個類, MyClass.SubClass.SubSubClass()在你的情況下不能工作的是MyClass.SubClass是一個Mock沒有SubSubClass定義。 簡單地讓FakeSubClassFakeSubClass繼承它FakeSubClass將解決問題。

您可以輕松地將MyClass修補到FakeClass ,您將擁有正確的測試對象而不是真實對象。

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()

我同意ZhouQuan有一個非常好的答案,因為它適用於MyClass.Subclass上的任何方法或變量。 也就是說,這里有一些可能有用或可能沒用的變化。

如果由於某種原因無法直接編輯FakeSubClass ,或者您只想繼承SubSubClass()而不是其他內容,則可以在test()更改它。

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

    FakeSubClass.SubSubClass = MyClass.SubClass.SubSubClass()

    MyClass.SubClass = Mock(side_effect=FakeSubClass)

    MyClass.SubClass()
    MyClass.SubClass.SubSubClass()

我認為可能值得注意的是, Mock確實接受了一個wraps參數,它可以用來獲得類似的行為,盡管它並不完全是你要求的。 這是一個例子。

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:
    def __init__(self) -> None:
        print("\nfake init called")

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

    MyClass.SubClass = Mock(side_effect=FakeSubClass, wraps=MyClass.SubClass)

    MyClass.SubClass()
    MyClass.SubClass.SubSubClass()

這給出了不同的輸出。

real sub init called

real sub sub init called

fake init called # A call to MyClass.SubClass() causes both real and fake inits.

real sub init called # Same MyClass.SubClass() call.

real sub sub init called # But now the SubSubClass() does resolve correctly.

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM