Reputation: 22518
I have one class (Cat) and one method (say_hello) to patch. When I patch the class only, everything works well. When I patch the method only, it works too. When I patch both at the same time, the class is not patched but the method is properly patched.
main.py
from hello import say_hello
from cat import Cat
cat = Cat('kitty')
def main():
print(say_hello())
hello.py
def say_hello():
return "No mocked"
test.py
import unittest
from unittest.mock import patch
class TestStringMethods(unittest.TestCase):
# cat.Cat is not patched correctly if both patch statements are there
@patch('cat.Cat')
@patch('main.say_hello')
def test_kitty(self, say_hello_mock, cat_mock):
say_hello_mock.return_value = "Mocked"
from main import main
main()
if __name__ == '__main__':
unittest.main()
If you run the previous example, a real Cat is created. If you comment the patch of main.say_hello, a mock Cat is created.
Upvotes: 3
Views: 96
Reputation: 2768
The say_hello()
is defined in hello.py
, so this:
@patch('main.say_hello')
should be changed to:
@patch('hello.say_hello')
then you could decide which to be mocked first in the decorators.
Upvotes: 0
Reputation: 22518
On my question, the first patch called is main.say_hello. I will trigger an import of the main module and the cat will be instantiated as a real Cat instance. Then Cat is patched but too late.
The trick is to invert the order of the patch:
@patch('main.say_hello')
@patch('cat.Cat')
def test_kitty(self, cat_mock, say_hello_mock):
say_hello_mock.return_value = "Mocked"
from main import main
main()
As decorators are called in an outward order, the Cat is patched then say_hello is patched triggering an import of the main module (which instantiates a mocked Cat).
Upvotes: 1
Reputation: 65
I don't know why the patch decorator is not working, but you can use this solution:
def test_kitty(self):
with patch('cat.Cat') as cat_mock:
with patch('main.say_hello') as hello_mock:
from main import main
main()
Upvotes: 1
Reputation: 14369
The @patch('cat.Cat')
call will at first import the cat
module. At this point the cat.cat
object is created from the original Cat
class and it will reference this original class as its type.
Now you are replacing the original reference to cat.Cat
with a reference to the mock. This has no effect on the object anymore.
You patch an attribute or method of the class and the instance will be change indirectly, but replacing the reference keeps the original class untouched.
Upvotes: 0