Milan
Milan

Reputation: 1750

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

I have the following 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()

I want to mock the factory method of Toto so when it's called, it returns a mock of Toto.

The following test should then pass:

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

Currently, I fail to properly mock the factory method.

Upvotes: 0

Views: 1330

Answers (1)

MrBean Bremen
MrBean Bremen

Reputation: 16855

The problem is that in your test, you don't use the mocked instance of Toto, which resides in the Toto module, but the one imported in the test itself (from ... import Toto). To fix this, there are 2 possibilities: use the mocked object, or mock the reference in the test itself.

To use the mocked object, you shall import the module containing the object to be mocked:

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"

If you import the object using from ... import, you have to patch the reference in the test module instead, with __name__ containing the name of the current module:

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"

If for some reason you need to mock both instances, as mentioned in the comments, you can also do this:

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"

Please read where to patch for more information.

Upvotes: 1

Related Questions