I came across a strange bahviour of the patch decorator in Fudge 1.0.3. It does not patch the module when importing classes via
from <module> import <class>
but works fine when importing
import <module>
with the corresponding code adaption.
Here's a minimalized setup:
mdle.py:
class Klaas(object):
def __init__(self):
# easyest way to signal this class has been instantiated accidently
raise Exception(">:[")
some.py (test not working):
from mdle import Klaas()
def instantiate():
instance = Klaas()
some.py (test working):
import mdle
def instantiate():
instance = mdle.Klaas()
some_test.py:
import unittest
import fudge
import some
class SomeTest(unittest.TestCase):
@fudge.patch("mdle.Klaas")
def test_somethingb(self, Klaas_mock):
klaas_inst = (Klaas_mock.expects_call()
.returns_fake())
# Receiving the exception
some.instantiate()
Should I patch in a different way? Is this a limitation of Fudge, or a bug?
You have to patch the name where the object is being referenced, not where it's being defined.
Remember modules are just objects with a dict of names pointing to other objects (a class is an object too). The same object can have multiple (possibly identical) names in different modules. Patching makes a name temporarily point to a
Fakerather than to the original object.I assume in your first (not working)
some.pymodule you meant:That creates a name
some.Klassthat gets used in that module. The name happens to match the name inmdleby default, but you actually have two names pointing to the same class object. It's the name insomeyou need to patch if you want to use a fake instead, because that's the name used to reference the class in the module under test.Your test patches
mdle.Klasswhich is not the name used insome, so your code still picks up the real class object using its own unpatched name. You need to patchsome.Klassinstead in this case.In your second (working)
some.pyyou import the wholemdlemodule and reference the class using the name in that module. That's why patchingmdle.Klassworks in that case, you're patching the name that's being used.