7

I want to mock a function which is called within a class method while testing the class method in a Django project. Consider the following structure:

app/utils.py

def func():
    ...
    return resp  # outcome is a HTTPResponse object

app/models.py

from app.utils import func

class MyModel(models.Model):
    
    # fields
    
    def call_func(self):
        ...
        func()
        ...

app/tests/test_my_model.py

from django.test import TestCase
import mock    

from app.models import MyModel

class MyModelTestCase(TestCase):

    fixtures = ['my_model_fixtures.json']

    def setUp(self):
        my_model = MyModel.objects.get(id=1)

    @mock.patch('app.utils.func')
    def fake_func(self):
        return mock.MagicMock(headers={'content-type': 'text/html'},
                              status_code=2000, 
                              content="Fake 200 Response"))

    def test_my_model(self):
        my_model.call_func()
        ...  # and asserting the parameters returned by func

When I run the test the mock function fake_func() is avoided and the real func() is called instead. I guess the scope in the mock.patch decorator might be wrong, but I couldn't find a way to make it work. What should I do?

crypdick
  • 16,152
  • 7
  • 51
  • 74
VahidM
  • 287
  • 1
  • 2
  • 14

2 Answers2

7

There are three problems with your code:

1) As Daniel Roseman mentioned, you need to patch the module where the function is called, not where it is defined.

2) In addition, you need to decorate the test method that will actually be executing the code that calls the mocked function.

3) Finally, you also need to pass the mocked version in as a parameter to your test method, probably something like this:

fake_response = mock.MagicMock(headers={'content-type': 'text/html'},
                          status_code=2000, 
                          content="Fake 200 Response"))


class MyModelTestCase(TestCase):

    fixtures = ['my_model_fixtures.json']

    def setUp(self):
        my_model = MyModel.objects.get(id=1)

    @mock.patch('app.models.func', return_value=fake_response)
    def test_my_model(self, fake_response):  # the mock goes in as a param or else you get number of arguments error!
        my_model.call_func()
        self.assertTrue(fake_response.called)
erewok
  • 7,555
  • 3
  • 33
  • 45
  • What if I need the side effects of a real mock function, not only a return value? for example in this case consider I need something to be printed when the function is mocked. – VahidM Apr 06 '15 at 06:24
  • and in the real context of this question I don't need the `fake_response` itself, I will parse it in `call_func` and update a field in `MyModel` regarding it. – VahidM Apr 06 '15 at 06:26
  • I reviewed it again, you're right, and I can even use `fake_func()` instead of `fake_response` as `return_value`. thanks! – VahidM Apr 06 '15 at 13:04
1

As the docs explain, you need to mock func in the place it is called, not where it is defined. So:

@mock.patch('app.models.func')
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895