I'm writing a basic Django app that integrates with a third party API. Here is the basic folder structure of the files involved:
timecardsite/
__init__.py
tests/
__init__.py
tests.py
services.py
views.py
...
The services.py file contains various functions that interact with the third party API directly. They typically return dictionaries with the data I need to use in the views.
In my tests file, specifically in the class where I test the views, I'm trying to mock the return value of a few of these service functions, so ideally they won't even be called (I test the service functions directly in another class):
tests.py
from unittest.mock import patch
from django.test import TestCase
import timecardsite.services as services
from timecardsite.models import Account
...
class ViewsTests(TestCase):
@patch('timecardsite.views.services.get_access_token')
@patch('timecardsite.views.services.get_account_info')
def test_auth_view_saves_tokens_and_account_info_to_db(self, mock_access, mock_account):
# Mock both get_access_token and get_account_info
code = generate_random_token()
access_token = generate_random_token()
refresh_token = generate_random_token()
account_id = generate_random_token(length=5)
name = 'Example name'
mock_access.return_value = {
'access_token': access_token,
'refresh_token': refresh_token
}
mock_account.return_value = {
'account_id': account_id,
'name': name
}
self.client.get(f'/auth/?code={code}')
self.assertEqual(Account.objects.count(), 1)
new_account = Account.objects.first()
self.assertEqual(new_account.account_id, account_id)
self.assertEqual(new_account.name, name)
self.assertEqual(new_account.access_token, access_token)
self.assertEqual(new_account.refresh_token, refresh_token)
The view where these functions are called:
views.py
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
import timecardsite.services as services
from timecardsite.models import Account
def auth(request):
# capture code from url
code = request.GET.get('code')
if code:
# request tokens
tokens = services.get_access_token(code)
# request account info
account_info = services.get_account_info(tokens['access_token'])
# create Account object
account = Account(
account_id = account_info['account_id'],
access_token = tokens['access_token'],
refresh_token = tokens['refresh_token'],
name = account_info['name']
)
account.save()
return HttpResponseRedirect(reverse('dashboard'))
This is the error I get - clearly the mocked service functions are being called, and are failing because it's trying to hit the actual API with a fake code I generated.
..........E
======================================================================
ERROR: test_auth_view_saves_tokens_and_account_info_to_db (timecardsite.tests.tests.ViewsTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/holden/.pyenv/versions/3.7.10/lib/python3.7/unittest/mock.py", line 1256, in patched
return func(*args, **keywargs)
File "/Users/holden/Projects/timesheet/timecardsite/tests/tests.py", line 193, in test_auth_view_saves_tokens_and_account_info_to_db
self.client.get(f'/auth/?code={code}')
File "/Users/holden/.pyenv/versions/timesheet/lib/python3.7/site-packages/django/test/client.py", line 739, in get
response = super().get(path, data=data, secure=secure, **extra)
File "/Users/holden/.pyenv/versions/timesheet/lib/python3.7/site-packages/django/test/client.py", line 395, in get
**extra,
File "/Users/holden/.pyenv/versions/timesheet/lib/python3.7/site-packages/django/test/client.py", line 470, in generic
return self.request(**r)
File "/Users/holden/.pyenv/versions/timesheet/lib/python3.7/site-packages/django/test/client.py", line 716, in request
self.check_exception(response)
File "/Users/holden/.pyenv/versions/timesheet/lib/python3.7/site-packages/django/test/client.py", line 577, in check_exception
raise exc_value
File "/Users/holden/.pyenv/versions/timesheet/lib/python3.7/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "/Users/holden/.pyenv/versions/timesheet/lib/python3.7/site-packages/django/core/handlers/base.py", line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/holden/Projects/timesheet/timecardsite/views.py", line 19, in auth
account_info = services.get_account_info(tokens['access_token'])
KeyError: 'access_token'
----------------------------------------------------------------------
Ran 11 tests in 0.074s
How do I successfully mock those functions?
Thanks.