Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the proper way of testing throttling in DRF?

What is the proper way of testing throttling in DRF? I coulnd't find out any answer to this question on the net. I want to have separate tests for each endpoint since each one has custom requests limits (ScopedRateThrottle).

The important thing is that it can't affect other tests - they have to somehow run without throttling and limiting.

like image 243
jmolinski Avatar asked Sep 03 '16 16:09

jmolinski


2 Answers

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')

like image 104
yofee Avatar answered Oct 16 '22 23:10

yofee


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.

like image 6
Daniel Dubovski Avatar answered Oct 16 '22 21:10

Daniel Dubovski