如何模拟工厂静态方法以便 return 模拟对象?

How to mock a factory static method in order to return a mocked object?

我有以下 class:

class Toto():

    @staticmethod
    def factory():
        return Toto("Toto")

    def __init__(self, name):
        self.name = name

    def get_name(self):
        return self.name

    @staticmethod
    def method_to_test():
      # do stuff
      toto_object = Toto.factory()
      # do stuff with toto_object
      toto_object.get_name()

我想要 mock Totofactory 方法,所以当它被调用时,它 returns 是 Toto 的模拟。

接下来的测试应该会通过:

from junit.toto import Toto


def test_toto_method_to_test(mocked):
    mocked_toto = mocker.patch("junit.toto.Toto")
    mocked_toto.get_name.return_value = "Tata"
    mocked_toto_factory = mocker.patch(__name__ + ".Toto.factory")
    mocked_toto_factory.return_value = mocked_junit_xml_generator

    Toto.method_to_test()
    mocked_toto.get_name.assert_called()

    #mocked_toto and mock in the tested method are not the same -> FAIL

目前,我无法正确模拟工厂方法。

问题在于,在您的测试中,您没有使用位于 Toto 模块中的 Toto 的模拟实例,而是在测试本身中导入的实例(from ... import Toto).要解决此问题,有两种可能性:使用模拟对象,或在测试本身中模拟引用。

要使用模拟对象,您应该导入包含要模拟的对象的模块:

import junit

def test_toto_mock(mocker):
    mocked_toto = mocker.patch("junit.toto.Toto")
    mocked_toto.get_name.return_value = "Tata"

    assert mocked_toto.get_name() == "Tata"

    mocked_toto.factory.return_value = mocked_toto

    # use the same reference that you have mocked
    assert junit.toto.Toto.factory().get_name() == "Tata"

如果使用 from ... import 导入对象,则必须在测试模块中修补引用,__name__ 包含当前模块的名称:

from junit.toto import ToTo

def test_toto_mock(mocker):
    # mock the reference in the test itself
    mocked_toto = mocker.patch(__name__ + ".Toto")
    mocked_toto.get_name.return_value = "Tata"

    assert mocked_toto.get_name() == "Tata"

    mocked_toto.factory.return_value = mocked_toto
    # use the local reference of ToTo
    assert Toto.factory().get_name() == "Tata"

如果由于某种原因你需要模拟两个实例,正如评论中提到的,你也可以这样做:

import junit
from junit.toto import ToTo

def test_toto_mock(mocker):
    mocked_toto = mocker.patch(__name__ + ".Toto")
    # replace the object in the other module with the same mock
    mocker.patch("junit.toto.Toto", mocked_toto)
    mocked_toto.get_name.return_value = "Tata"

    assert mocked_toto.get_name() == "Tata"

    mocked_toto.factory.return_value = mocked_toto

    assert Toto.factory().get_name() == "Tata"
    assert junit.toto.Toto.factory().get_name() == "Tata"

请阅读 where to patch 了解更多信息。