I always thought that you could mock a method on a class only when the class it is coming from is not instantiated in the code under test. And if it is you have to mock the class (just as you have to do for builtins).
But today I made a "mistake" and saw that it just worked. So now I'm confused on when to use mocking a full class and when to use mocking a method on a class.
Let's assume I have the following 3 example files:
Engine.py
:
class Engine(object):
def __init__(self, start_temperature):
self._started = False
self._temperature = start_temperature
def start(self):
self._started = True
self._set_temperature(10)
def stop(self):
self._started = False
self._set_temperature(-10)
def get_temperature(self):
return self._temperature
def _set_temperature(self, value):
self._temperature += value
MyClass.py
:
from Engine import Engine
class MyClass(object):
def __init__(self):
self.engine = Engine(0)
def check_start(self):
self.engine.start()
def check_stop(self):
self.engine2 = Engine(1)
self.engine2.stop()
def get_temperature(self):
return self.engine.get_temperature()
and finally the actual test: test_MyClass.py
:
import unittest
import unittest.mock as mock
import MyClass
class TestMyClass(unittest.TestCase):
def test__initiation_returns_0_temperature(self):
c = MyClass.MyClass()
temperature = c.get_temperature()
self.assertEqual(temperature, 0)
@mock.patch("MyClass.Engine")
def test_engine_get_temperature_mocked_via_class(self, mock_Engine_cls):
mock_Engine_cls.return_value.get_temperature.return_value = 11
c = MyClass.MyClass()
c.check_start()
temperature = c.get_temperature()
self.assertEqual(temperature, 11)
@mock.patch("MyClass.Engine.get_temperature")
def test_engine_get_temperature_mocked_via_method(self, get_temperature_mock):
get_temperature_mock.return_value = 12
c = MyClass.MyClass()
c.check_stop()
temperature = c.get_temperature()
self.assertEqual(temperature, 12)
if __name__ == "__main__":
unittest.main()
I would have expected the 3rd test to error since in the past I got errors for instantiated classes if I did not mock the full class like done in the 2nd test case, but now it passes.
Why is that?
I always thought that you could mock a method on a class only when the class it is coming from is not instantiated in the code under test. And if it is you have to mock the class (just as you have to do for builtins).
But today I made a "mistake" and saw that it just worked. So now I'm confused on when to use mocking a full class and when to use mocking a method on a class.
Let's assume I have the following 3 example files:
Engine.py
:
class Engine(object):
def __init__(self, start_temperature):
self._started = False
self._temperature = start_temperature
def start(self):
self._started = True
self._set_temperature(10)
def stop(self):
self._started = False
self._set_temperature(-10)
def get_temperature(self):
return self._temperature
def _set_temperature(self, value):
self._temperature += value
MyClass.py
:
from Engine import Engine
class MyClass(object):
def __init__(self):
self.engine = Engine(0)
def check_start(self):
self.engine.start()
def check_stop(self):
self.engine2 = Engine(1)
self.engine2.stop()
def get_temperature(self):
return self.engine.get_temperature()
and finally the actual test: test_MyClass.py
:
import unittest
import unittest.mock as mock
import MyClass
class TestMyClass(unittest.TestCase):
def test__initiation_returns_0_temperature(self):
c = MyClass.MyClass()
temperature = c.get_temperature()
self.assertEqual(temperature, 0)
@mock.patch("MyClass.Engine")
def test_engine_get_temperature_mocked_via_class(self, mock_Engine_cls):
mock_Engine_cls.return_value.get_temperature.return_value = 11
c = MyClass.MyClass()
c.check_start()
temperature = c.get_temperature()
self.assertEqual(temperature, 11)
@mock.patch("MyClass.Engine.get_temperature")
def test_engine_get_temperature_mocked_via_method(self, get_temperature_mock):
get_temperature_mock.return_value = 12
c = MyClass.MyClass()
c.check_stop()
temperature = c.get_temperature()
self.assertEqual(temperature, 12)
if __name__ == "__main__":
unittest.main()
I would have expected the 3rd test to error since in the past I got errors for instantiated classes if I did not mock the full class like done in the 2nd test case, but now it passes.
Why is that?
Share Improve this question edited Mar 13 at 13:27 Nemelis asked Mar 3 at 14:54 NemelisNemelis 5,4902 gold badges22 silver badges42 bronze badges 3 |1 Answer
Reset to default 0You are naming, one of the files with first letter in lowercase, then you should fix the imports.
As the filename is engine.py you should import from engine import Engine
in MyClass.py. I've just changed it, tested your code and it worked flawlessly.
self.engine2
), you have to usemock.patch.object
- though that can only mock the object after it is created. – MrBean Bremen Commented Mar 5 at 19:54