Issue
I have a utils class that besides other stuff has a function to get an id:
# utils.py
def get_id():
return ...
Also, there is another class, lets say its about cars, that uses this function:
# car.py
from utils import get_id
class Car:
...
def setup(self):
self.id = get_id()
I want to run a unittest that should provide a specific id which is why I am mocking this function:
# test_car.py
import unittest
from unittest.Mock import Mock
import utils
class TestCar(unittest.TestCase):
...
def test_car_setup(self):
utils.get_id = Mock(callable, return_value="my-testing-id")
car = Car()
car.setup()
...
But this mock is not working. I still get the behavior of the original function. However, I was able to get it working by adjusting the car class like this:
# car.py
from . import utils
class Car:
...
def setup(self):
self.id = utils.get_id()
The question is now, why is that working, and the previous version not? I initially thought that this could be a call by reference/call by value issue, but the debugger showed that now matter how I adjust the imports, the utils function is always the same object.
I understand that this might not be a typical question because my projest is working, but I am failing to understand the workings of python that lead to it and I would appreciate if someone could enlighten me.
Thanks in advance!
Solution
Change test_car.py
It is enough to make some changes to your file test_car.py
, while car.py
and utils.py
remain without changes; the file car.py
include the original import from utils import get_id
as showed below:
# car.py
from utils import get_id
class Car:
...
def setup(self):
self.id = get_id()
Below I show you the file test_car.py
with the needed modifications:
#test_car.py
import unittest
from unittest.mock import Mock
#import utils # <----- this import is now useless
from car import Car # <----- add this import
import car as module_car # <----- add this import (added the alias
# module_car to avoid name collision)
class TestCar(unittest.TestCase):
def test_car_setup(self):
# I have commented your instruction
#utils.get_id = Mock(callable, return_value="my-testing-id")
# USE THIS INSTRUCTION
module_car.get_id = Mock(callable, return_value="my-testing-id")
car = Car()
car.setup()
if __name__ == '__main__':
unittest.main()
Explanation
The most important change is the following:
utils.get_id = Mock(callable, return_value="my-testing-id") ---> module_car.get_id = Mock(callable, return_value="my-testing-id")
The instruction
module_car.get_id = Mock(...)
substitutes the object pointed by the nameget_id
used inside the modulecar.py
; this means that when is executed the instructionself.id = get_id()
in the methodsetup()
is executed the Mock object which returnsmy-testing-id
Your instruction (
utils.get_id = Mock(...)
) substitutes the object pointed by nameget_id
, but inside the fileutils.py
; the error is that the object used by the methodsetup()
is pointed by the nameget_id
defined insidecar.py
.
Why your second solution works
Your second solution is using the import from . import utils
in the file car.py
as showed below:
# car.py
from . import utils
class Car:
...
def setup(self):
self.id = utils.get_id()
In this way, in your test code, the object pointed by utils.get_id()
has been substituted by the Mock object (this is thanks to the instruction utils.get_id = Mock(callable, return_value="my-testing-id")
present in your code).
So it happens:
utils.get_id() returns a reference which (points to) ----> Mock object
In the file car.py
you use exactly the instruction: utils.get_id()
and it returns a reference to the Mock.
Why your first solution doesn't work
The file test_car.py
is the same both in the first and second solutions, so it is always true that:
utils.get_id() returns a reference which (points to) ----> Mock object
But your original file car.py
is:
# car.py
from utils import get_id
class Car:
...
def setup(self):
self.id = get_id()
So in it, with the instruction from utils import get_id
you define the name get_id
and get_id()
returns a reference to the object get_id
defined inside the module utils.py
.
In the method setup()
you use directly get_id()
(and not utils.get_id()
); get_id()
returns the reference to the real object and not to the mock object; the Mock object is returned only by utils.get_id()
.
Answered By - User051209
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.