1

I am writing System Tests for my Django app, where I test the complete application via HTTP requests and mock its external dependencies' APIs.

In views.py I have something like:

from external_service import ExternalService
externalService = ExternalService
data = externalService.get_data()

@crsf_exempt
def endpoint(request):
    do_something()

What I want is to mock (or stub) ExternalService to return a predefined response when its method get_data() is called.

The problem is that when I run python manage.py test, views.py is loaded before my test class. So when I patch the object with a mocked one, the function get_data() was already called.

This solution didn't work either.

Nazim Kerimbekov
  • 4,712
  • 8
  • 34
  • 58
victortv
  • 7,874
  • 2
  • 23
  • 27

1 Answers1

0

First off, don't call your method at import time. That can't be necessary, surely?

If get_data does something like a get request, e.g.

def get_data():
    response = requests.get(DATA_URL)
    if response.ok:
        return response
    else:
        return None

Then you can mock it;

from unittest.mock import Mock, patch

from nose.tools import assert_is_none, assert_list_equal

from external_service import ExternalService


@patch('external_service.requests.get')
def test_getting_data(mock_get):
    data = [{
        'content': 'Response data'
    }]

    mock_get.return_value = Mock(ok=True)
    mock_get.return_value.json.return_value = data

    response = ExternalService.get_data()

    assert_list_equal(response.json(), data)


@patch('external_service.requests.get')
def test_getting_data_error(mock_get):
    mock_get.return_value.ok = False

    response = ExternalService.get_data()

    assert_is_none(response)

For this you'll need pip install nose if you don't already have it.

markwalker_
  • 12,078
  • 7
  • 62
  • 99
  • I think this only works after I call an endpoint at my views.py during tests. I am calling the method during import time because it downloads several files from a private repo. It is a task that only needs to be done from time to time, and it's too time consuming to call it on every method that I need those files. What I can do is changing my working code to do something like `if (settings.DEBUG==True): data = fake_data` but I guess this isn't a good practice. – victortv Apr 09 '19 at 23:03
  • Well this approach should allow you to fake whatever `get_data` does. But as I say, that's assuming it does something fairly simple like a `get` request. – markwalker_ Apr 09 '19 at 23:10