What is the proper way of testing throttling in DRF?
Like people already mentioned, this doesn't exactly fall within the scope of unit tests, but still, how about simply doing something like this:
from django.core.urlresolvers import reverse
from django.test import override_settings
from rest_framework.test import APITestCase, APIClient
class ThrottleApiTests(APITestCase):
# make sure to override your settings for testing
TESTING_THRESHOLD = '5/min'
# THROTTLE_THRESHOLD is the variable that you set for DRF DEFAULT_THROTTLE_RATES
@override_settings(THROTTLE_THRESHOLD=TESTING_THRESHOLD)
def test_check_health(self):
client = APIClient()
# some end point you want to test (in this case it's a public enpoint that doesn't require authentication
_url = reverse('check-health')
# this is probably set in settings in you case
for i in range(0, self.TESTING_THRESHOLD):
client.get(_url)
# this call should err
response = client.get(_url)
# 429 - too many requests
self.assertEqual(response.status_code, 429)
Also, regarding your concerns of side-effects, as long as you do user creation in setUp
or setUpTestData
, tests will be isolated (as they should), so no need to worry about 'dirty' data or scope in that sense.
Regarding cache clearing between tests, I would just add cache.clear()
in tearDown
or try and clear the specific key defined for throttling.
An easy solution is to patch
the get_rate
method of your throttle class. Thanks to tprestegard for this comment!
I have a custom class in my case:
from rest_framework.throttling import UserRateThrottle
class AuthRateThrottle(UserRateThrottle):
scope = 'auth'
In your tests:
from unittest.mock import patch
from django.core.cache import cache
from rest_framework import status
class Tests(SimpleTestCase):
def setUp(self):
cache.clear()
@patch('path.to.AuthRateThrottle.get_rate')
def test_throttling(self, mock):
mock.return_value = '1/day'
response = self.client.post(self.url, {})
self.assertEqual(
response.status_code,
status.HTTP_400_BAD_REQUEST, # some fields are required
)
response = self.client.post(self.url, {})
self.assertEqual(
response.status_code,
status.HTTP_429_TOO_MANY_REQUESTS,
)
It is also possible to patch the method in the DRF package to change the behavior of the standard throttle classes: @patch('rest_framework.throttling.SimpleRateThrottle.get_rate')