3

How can I rewrite 'user': '5/minute' on 'user': '2/day' in the settings for the test

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
         'user': '5/minute'
    }
}

How can i do this with @override_settings or mb with context manage? I need to apply this only in one test, I can not rewrite the whole dictionary

def test_mytest(self):
    value = settings.REST_FRAMEWORK
    value['DEFAULT_THROTTLE_RATES'] = {'user':'2/day'}

    data1 = {}
    data2 = {}
    data3 = {}

with self.settings(REST_FRAMEWORK=value):
    resp1 = self.client.post(self.url, data1, format='json')
    resp2 = self.client.post(self.url, data1, format='json')
    resp3 = self.client.post(self.url, data1, format='json')

assert resp3.status_code == 429, resp3.data

But no have 429 error, although the value has changed

Roman Nozhenko
  • 698
  • 8
  • 21

3 Answers3

4
    from unittest import mock

    @mock.patch('rest_framework.throttling.SimpleRateThrottle.get_rate')
    def test_api(self, get_rate):
        get_rate.return_value = '1/minute'

        from rest_framework.settings import api_settings
        print(api_settings.DEFAULT_THROTTLE_RATES)
        print(api_settings.user_settings)


        url = 'api'

        response = self.client.get(url)
        self.assertEqual(response.status_code, HTTP_200_OK)

        response = self.client.get(url)
        self.assertEqual(response.status_code, HTTP_429_TOO_MANY_REQUESTS)
cảnh nguyễn
  • 528
  • 1
  • 6
  • 6
3

Solution 1: With the @override_default() wrapper

You want to apply a wrapper function to the viewset that you wish to override with this value:

from rest_framework.throttling import UserRateThrottle

class UserViewSet(viewsets.ViewSet):

    @api_view(['PUT'])
    @throttle_classes([UserRateThrottle])
    @override_settings(REST_FRAMEWORK['DEFAULT_THROTTLE_RATES']['user'] = "2/day")
    def update(self, request, pk=None):
        ...

Add the wrapper before any view or viewset you wish to apply this overridden customs value.

You also want this in your settings.py:

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ),
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '13500/day'
    }
}

Solution 2: Custom throttling class

However, if you want different throttling rates for when you're in a test environment, perhaps try the following in settings.py:

TEST = True ## <-- Set to False when not in testing!!

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.CustomUserRateThrottle'
    )
}

And designate your own custom throttling class:

from django.conf import settings

class CustomUserRateThrottle(throttling.UserRateThrottle):
    if settings.TEST:
        THROTTLE_RATES = 'DEFAULT_THROTTLE_RATES': {
            'user': '13500/day',
        }
    else:
        THROTTLE_RATES = 'DEFAULT_THROTTLE_RATES': {
            'user': '2/day',
        } 
    return settings.TEST # <-- Custom throttling classes must always return either True or False, so this makes sense.

Solution 3: directly in your "testing shell":

Include your custom wrapper as we defined above but this time in your test_something() method in test.py:

from django.conf import settings
from django.test import TestCase

class TestCase(TestCase):

    def test_something(self):

       with self.settings(REST_FRAMEWORK = ['DEFAULT_THROTTLE_RATES']['user'] = '13500/day'):


       # START TESTING HERE WITH TEST SETTINGS
  • 1
    I need this only inside the test and this syntax is incorrect( – Roman Nozhenko Aug 27 '18 at 10:34
  • Hmmm, not sure what you mean by the syntax being incorrect, maybe you could have a `TEST = True` flag in settings.py. I will add this to my answer. –  Aug 27 '18 at 10:37
  • @RomanKovalevsky Hi Roman - I've added a few solutions you could leverage in DRF project - I feel solution 3 is the most elegant. I've also decided to use `13500/day` to keep the base unit the same (rather than 5/minute). –  Aug 27 '18 at 10:58
  • with self.settings(REST_FRAMEWORK = ['DEFAULT_THROTTLE_RATES']['user'] = '13500/day') - ',' or ')' expected. SyntaxError: invalid syntax – Roman Nozhenko Aug 27 '18 at 11:47
  • 2
    E with self.settings(REST_FRAMEWORK = ['DEFAULT_THROTTLE_RATES']['user']="2/day"): E ^ E SyntaxError: invalid syntax – Roman Nozhenko Aug 27 '18 at 11:50
  • I can write this in test settings.REST_FRAMEWORK['DEFAULT_THROTTLE_RATES']['user'] = '2/day', end no error, but nit override in decorator – Roman Nozhenko Aug 27 '18 at 11:59
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/178854/discussion-between-roman-kovalevsky-and-micheal-j-roberts). – Roman Nozhenko Aug 27 '18 at 11:59
  • Hi Roman...simply add that line to what ever test you have setup. Can I see your test.py code please? –  Aug 27 '18 at 12:03
  • thanks for your help, i just used a mock and it solved my problem) – Roman Nozhenko Aug 27 '18 at 13:17
  • Ideal - let me know if this helped you find the answer, don’t forget to tick this one off if you’re happy it helped you sufficiently! –  Aug 27 '18 at 13:21
  • @RomanNozhenko same here – H. Qasemi Jan 20 '22 at 08:42
2

Depending on your use case you can follow one of the following ways :

Suggestion 1 : If you want to override settings in few specific test cases the you can use @override_settings decorator. But this make no sense if you want that for all test cases, if that's the case then you can follow suggestion 2.

Suggestion 2 : This is more generic and efficient solution.

You can have several settings file for various running environment (inside settings python module) something like this:

  1. base.py (containing all of your basic settings)
  2. local.py (import * from base here and override/add settings specific to local/dev environment)
  3. test.py (import * from base here and override/add settings specific to test environment)
  4. you can also have a settings specific for production environment in prod.py or something.

With this setup can run tests like python manage.py test --settings=yourproject.settings.test

Umair Mohammad
  • 4,489
  • 2
  • 20
  • 34
  • 2
    Yep - and then it's just a case of using: `python manage.py test --settings=app.settings.test` –  Aug 27 '18 at 11:03
  • 1
    yes, the decision to specify the desired dictionary in the test settings works, I'm not sure the truth that I will be taken ( – Roman Nozhenko Aug 27 '18 at 11:43