mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-25 19:14:01 +03:00
Formalize URLPatternsTestCase (#5703)
* Add formalized URLPatternsTestCase * Update versioning tests w/ new URLPatternsTestCase * Cleanup router tests urlpatterns * Add docs for URLPatternsTestCase
This commit is contained in:
parent
6b0bf72bb8
commit
b65967711c
|
@ -292,7 +292,7 @@ similar way as with `RequestsClient`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Test cases
|
# API Test cases
|
||||||
|
|
||||||
REST framework includes the following test case classes, that mirror the existing Django test case classes, but use `APIClient` instead of Django's default `Client`.
|
REST framework includes the following test case classes, that mirror the existing Django test case classes, but use `APIClient` instead of Django's default `Client`.
|
||||||
|
|
||||||
|
@ -324,6 +324,32 @@ You can use any of REST framework's test case classes as you would for the regul
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
# URLPatternsTestCase
|
||||||
|
|
||||||
|
REST framework also provides a test case class for isolating `urlpatterns` on a per-class basis. Note that this inherits from Django's `SimpleTestCase`, and will most likely need to be mixed with another test case class.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
from django.urls import include, path, reverse
|
||||||
|
from rest_framework.test import APITestCase, URLPatternsTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class AccountTests(APITestCase, URLPatternsTestCase):
|
||||||
|
urlpatterns = [
|
||||||
|
path('api/', include('api.urls')),
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_create_account(self):
|
||||||
|
"""
|
||||||
|
Ensure we can create a new account object.
|
||||||
|
"""
|
||||||
|
url = reverse('account-list')
|
||||||
|
response = self.client.get(url, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(len(response.data), 1)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# Testing responses
|
# Testing responses
|
||||||
|
|
||||||
## Checking the response data
|
## Checking the response data
|
||||||
|
|
|
@ -5,11 +5,12 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import io
|
import io
|
||||||
|
from importlib import import_module
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.core.handlers.wsgi import WSGIHandler
|
from django.core.handlers.wsgi import WSGIHandler
|
||||||
from django.test import testcases
|
from django.test import override_settings, testcases
|
||||||
from django.test.client import Client as DjangoClient
|
from django.test.client import Client as DjangoClient
|
||||||
from django.test.client import RequestFactory as DjangoRequestFactory
|
from django.test.client import RequestFactory as DjangoRequestFactory
|
||||||
from django.test.client import ClientHandler
|
from django.test.client import ClientHandler
|
||||||
|
@ -358,3 +359,44 @@ class APISimpleTestCase(testcases.SimpleTestCase):
|
||||||
|
|
||||||
class APILiveServerTestCase(testcases.LiveServerTestCase):
|
class APILiveServerTestCase(testcases.LiveServerTestCase):
|
||||||
client_class = APIClient
|
client_class = APIClient
|
||||||
|
|
||||||
|
|
||||||
|
class URLPatternsTestCase(testcases.SimpleTestCase):
|
||||||
|
"""
|
||||||
|
Isolate URL patterns on a per-TestCase basis. For example,
|
||||||
|
|
||||||
|
class ATestCase(URLPatternsTestCase):
|
||||||
|
urlpatterns = [...]
|
||||||
|
|
||||||
|
def test_something(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
class AnotherTestCase(URLPatternsTestCase):
|
||||||
|
urlpatterns = [...]
|
||||||
|
|
||||||
|
def test_something_else(self):
|
||||||
|
...
|
||||||
|
"""
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
# Get the module of the TestCase subclass
|
||||||
|
cls._module = import_module(cls.__module__)
|
||||||
|
cls._override = override_settings(ROOT_URLCONF=cls.__module__)
|
||||||
|
|
||||||
|
if hasattr(cls._module, 'urlpatterns'):
|
||||||
|
cls._module_urlpatterns = cls._module.urlpatterns
|
||||||
|
|
||||||
|
cls._module.urlpatterns = cls.urlpatterns
|
||||||
|
|
||||||
|
cls._override.enable()
|
||||||
|
super(URLPatternsTestCase, cls).setUpClass()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
super(URLPatternsTestCase, cls).tearDownClass()
|
||||||
|
cls._override.disable()
|
||||||
|
|
||||||
|
if hasattr(cls, '_module_urlpatterns'):
|
||||||
|
cls._module.urlpatterns = cls._module_urlpatterns
|
||||||
|
else:
|
||||||
|
del cls._module.urlpatterns
|
||||||
|
|
|
@ -14,7 +14,7 @@ from rest_framework.compat import get_regex_pattern
|
||||||
from rest_framework.decorators import detail_route, list_route
|
from rest_framework.decorators import detail_route, list_route
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.routers import DefaultRouter, SimpleRouter
|
from rest_framework.routers import DefaultRouter, SimpleRouter
|
||||||
from rest_framework.test import APIRequestFactory
|
from rest_framework.test import APIRequestFactory, URLPatternsTestCase
|
||||||
from rest_framework.utils import json
|
from rest_framework.utils import json
|
||||||
|
|
||||||
factory = APIRequestFactory()
|
factory = APIRequestFactory()
|
||||||
|
@ -90,23 +90,10 @@ namespaced_router.register(r'example', MockViewSet, base_name='example')
|
||||||
|
|
||||||
empty_prefix_router = SimpleRouter()
|
empty_prefix_router = SimpleRouter()
|
||||||
empty_prefix_router.register(r'', EmptyPrefixViewSet, base_name='empty_prefix')
|
empty_prefix_router.register(r'', EmptyPrefixViewSet, base_name='empty_prefix')
|
||||||
empty_prefix_urls = [
|
|
||||||
url(r'^', include(empty_prefix_router.urls)),
|
|
||||||
]
|
|
||||||
|
|
||||||
regex_url_path_router = SimpleRouter()
|
regex_url_path_router = SimpleRouter()
|
||||||
regex_url_path_router.register(r'', RegexUrlPathViewSet, base_name='regex')
|
regex_url_path_router.register(r'', RegexUrlPathViewSet, base_name='regex')
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
url(r'^non-namespaced/', include(namespaced_router.urls)),
|
|
||||||
url(r'^namespaced/', include((namespaced_router.urls, 'example'), namespace='example')),
|
|
||||||
url(r'^example/', include(notes_router.urls)),
|
|
||||||
url(r'^example2/', include(kwarged_notes_router.urls)),
|
|
||||||
|
|
||||||
url(r'^empty-prefix/', include(empty_prefix_urls)),
|
|
||||||
url(r'^regex/', include(regex_url_path_router.urls))
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class BasicViewSet(viewsets.ViewSet):
|
class BasicViewSet(viewsets.ViewSet):
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
|
@ -156,8 +143,12 @@ class TestSimpleRouter(TestCase):
|
||||||
assert route.mapping[method] == endpoint
|
assert route.mapping[method] == endpoint
|
||||||
|
|
||||||
|
|
||||||
@override_settings(ROOT_URLCONF='tests.test_routers')
|
class TestRootView(URLPatternsTestCase, TestCase):
|
||||||
class TestRootView(TestCase):
|
urlpatterns = [
|
||||||
|
url(r'^non-namespaced/', include(namespaced_router.urls)),
|
||||||
|
url(r'^namespaced/', include((namespaced_router.urls, 'namespaced'), namespace='namespaced')),
|
||||||
|
]
|
||||||
|
|
||||||
def test_retrieve_namespaced_root(self):
|
def test_retrieve_namespaced_root(self):
|
||||||
response = self.client.get('/namespaced/')
|
response = self.client.get('/namespaced/')
|
||||||
assert response.data == {"example": "http://testserver/namespaced/example/"}
|
assert response.data == {"example": "http://testserver/namespaced/example/"}
|
||||||
|
@ -167,11 +158,15 @@ class TestRootView(TestCase):
|
||||||
assert response.data == {"example": "http://testserver/non-namespaced/example/"}
|
assert response.data == {"example": "http://testserver/non-namespaced/example/"}
|
||||||
|
|
||||||
|
|
||||||
@override_settings(ROOT_URLCONF='tests.test_routers')
|
class TestCustomLookupFields(URLPatternsTestCase, TestCase):
|
||||||
class TestCustomLookupFields(TestCase):
|
|
||||||
"""
|
"""
|
||||||
Ensure that custom lookup fields are correctly routed.
|
Ensure that custom lookup fields are correctly routed.
|
||||||
"""
|
"""
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^example/', include(notes_router.urls)),
|
||||||
|
url(r'^example2/', include(kwarged_notes_router.urls)),
|
||||||
|
]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
RouterTestModel.objects.create(uuid='123', text='foo bar')
|
RouterTestModel.objects.create(uuid='123', text='foo bar')
|
||||||
RouterTestModel.objects.create(uuid='a b', text='baz qux')
|
RouterTestModel.objects.create(uuid='a b', text='baz qux')
|
||||||
|
@ -219,12 +214,17 @@ class TestLookupValueRegex(TestCase):
|
||||||
|
|
||||||
|
|
||||||
@override_settings(ROOT_URLCONF='tests.test_routers')
|
@override_settings(ROOT_URLCONF='tests.test_routers')
|
||||||
class TestLookupUrlKwargs(TestCase):
|
class TestLookupUrlKwargs(URLPatternsTestCase, TestCase):
|
||||||
"""
|
"""
|
||||||
Ensure the router honors lookup_url_kwarg.
|
Ensure the router honors lookup_url_kwarg.
|
||||||
|
|
||||||
Setup a deep lookup_field, but map it to a simple URL kwarg.
|
Setup a deep lookup_field, but map it to a simple URL kwarg.
|
||||||
"""
|
"""
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^example/', include(notes_router.urls)),
|
||||||
|
url(r'^example2/', include(kwarged_notes_router.urls)),
|
||||||
|
]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
RouterTestModel.objects.create(uuid='123', text='foo bar')
|
RouterTestModel.objects.create(uuid='123', text='foo bar')
|
||||||
|
|
||||||
|
@ -408,8 +408,11 @@ class TestDynamicListAndDetailRouter(TestCase):
|
||||||
self._test_list_and_detail_route_decorators(SubDynamicListAndDetailViewSet)
|
self._test_list_and_detail_route_decorators(SubDynamicListAndDetailViewSet)
|
||||||
|
|
||||||
|
|
||||||
@override_settings(ROOT_URLCONF='tests.test_routers')
|
class TestEmptyPrefix(URLPatternsTestCase, TestCase):
|
||||||
class TestEmptyPrefix(TestCase):
|
urlpatterns = [
|
||||||
|
url(r'^empty-prefix/', include(empty_prefix_router.urls)),
|
||||||
|
]
|
||||||
|
|
||||||
def test_empty_prefix_list(self):
|
def test_empty_prefix_list(self):
|
||||||
response = self.client.get('/empty-prefix/')
|
response = self.client.get('/empty-prefix/')
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
@ -422,8 +425,11 @@ class TestEmptyPrefix(TestCase):
|
||||||
assert json.loads(response.content.decode('utf-8')) == {'uuid': '111', 'text': 'First'}
|
assert json.loads(response.content.decode('utf-8')) == {'uuid': '111', 'text': 'First'}
|
||||||
|
|
||||||
|
|
||||||
@override_settings(ROOT_URLCONF='tests.test_routers')
|
class TestRegexUrlPath(URLPatternsTestCase, TestCase):
|
||||||
class TestRegexUrlPath(TestCase):
|
urlpatterns = [
|
||||||
|
url(r'^regex/', include(regex_url_path_router.urls)),
|
||||||
|
]
|
||||||
|
|
||||||
def test_regex_url_path_list(self):
|
def test_regex_url_path_list(self):
|
||||||
kwarg = '1234'
|
kwarg = '1234'
|
||||||
response = self.client.get('/regex/list/{}/'.format(kwarg))
|
response = self.client.get('/regex/list/{}/'.format(kwarg))
|
||||||
|
@ -438,8 +444,11 @@ class TestRegexUrlPath(TestCase):
|
||||||
assert json.loads(response.content.decode('utf-8')) == {'pk': pk, 'kwarg': kwarg}
|
assert json.loads(response.content.decode('utf-8')) == {'pk': pk, 'kwarg': kwarg}
|
||||||
|
|
||||||
|
|
||||||
@override_settings(ROOT_URLCONF='tests.test_routers')
|
class TestViewInitkwargs(URLPatternsTestCase, TestCase):
|
||||||
class TestViewInitkwargs(TestCase):
|
urlpatterns = [
|
||||||
|
url(r'^example/', include(notes_router.urls)),
|
||||||
|
]
|
||||||
|
|
||||||
def test_suffix(self):
|
def test_suffix(self):
|
||||||
match = resolve('/example/notes/')
|
match = resolve('/example/notes/')
|
||||||
initkwargs = match.func.initkwargs
|
initkwargs = match.func.initkwargs
|
||||||
|
|
|
@ -12,7 +12,7 @@ from rest_framework import fields, serializers
|
||||||
from rest_framework.decorators import api_view
|
from rest_framework.decorators import api_view
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.test import (
|
from rest_framework.test import (
|
||||||
APIClient, APIRequestFactory, force_authenticate
|
APIClient, APIRequestFactory, URLPatternsTestCase, force_authenticate
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -283,3 +283,30 @@ class TestAPIRequestFactory(TestCase):
|
||||||
content_type='application/json',
|
content_type='application/json',
|
||||||
)
|
)
|
||||||
assert request.META['CONTENT_TYPE'] == 'application/json'
|
assert request.META['CONTENT_TYPE'] == 'application/json'
|
||||||
|
|
||||||
|
|
||||||
|
class TestUrlPatternTestCase(URLPatternsTestCase):
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^$', view),
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
assert urlpatterns is not cls.urlpatterns
|
||||||
|
super(TestUrlPatternTestCase, cls).setUpClass()
|
||||||
|
assert urlpatterns is cls.urlpatterns
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
assert urlpatterns is cls.urlpatterns
|
||||||
|
super(TestUrlPatternTestCase, cls).tearDownClass()
|
||||||
|
assert urlpatterns is not cls.urlpatterns
|
||||||
|
|
||||||
|
def test_urlpatterns(self):
|
||||||
|
assert self.client.get('/').status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
class TestExistingPatterns(TestCase):
|
||||||
|
def test_urlpatterns(self):
|
||||||
|
# sanity test to ensure that this test module does not have a '/' route
|
||||||
|
assert self.client.get('/').status_code == 404
|
||||||
|
|
|
@ -7,33 +7,12 @@ from rest_framework.decorators import APIView
|
||||||
from rest_framework.relations import PKOnlyObject
|
from rest_framework.relations import PKOnlyObject
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.reverse import reverse
|
from rest_framework.reverse import reverse
|
||||||
from rest_framework.test import APIRequestFactory, APITestCase
|
from rest_framework.test import (
|
||||||
|
APIRequestFactory, APITestCase, URLPatternsTestCase
|
||||||
|
)
|
||||||
from rest_framework.versioning import NamespaceVersioning
|
from rest_framework.versioning import NamespaceVersioning
|
||||||
|
|
||||||
|
|
||||||
@override_settings(ROOT_URLCONF='tests.test_versioning')
|
|
||||||
class URLPatternsTestCase(APITestCase):
|
|
||||||
"""
|
|
||||||
Isolates URL patterns used during testing on the test class itself.
|
|
||||||
For example:
|
|
||||||
|
|
||||||
class MyTestCase(URLPatternsTestCase):
|
|
||||||
urlpatterns = [
|
|
||||||
...
|
|
||||||
]
|
|
||||||
|
|
||||||
def test_something(self):
|
|
||||||
...
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
global urlpatterns
|
|
||||||
urlpatterns = self.urlpatterns
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
global urlpatterns
|
|
||||||
urlpatterns = []
|
|
||||||
|
|
||||||
|
|
||||||
class RequestVersionView(APIView):
|
class RequestVersionView(APIView):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
return Response({'version': request.version})
|
return Response({'version': request.version})
|
||||||
|
@ -163,7 +142,7 @@ class TestRequestVersion:
|
||||||
assert response.data == {'version': None}
|
assert response.data == {'version': None}
|
||||||
|
|
||||||
|
|
||||||
class TestURLReversing(URLPatternsTestCase):
|
class TestURLReversing(URLPatternsTestCase, APITestCase):
|
||||||
included = [
|
included = [
|
||||||
url(r'^namespaced/$', dummy_view, name='another'),
|
url(r'^namespaced/$', dummy_view, name='another'),
|
||||||
url(r'^example/(?P<pk>\d+)/$', dummy_pk_view, name='example-detail')
|
url(r'^example/(?P<pk>\d+)/$', dummy_pk_view, name='example-detail')
|
||||||
|
@ -329,7 +308,7 @@ class TestAllowedAndDefaultVersion:
|
||||||
assert response.data == {'version': 'v2'}
|
assert response.data == {'version': 'v2'}
|
||||||
|
|
||||||
|
|
||||||
class TestHyperlinkedRelatedField(URLPatternsTestCase):
|
class TestHyperlinkedRelatedField(URLPatternsTestCase, APITestCase):
|
||||||
included = [
|
included = [
|
||||||
url(r'^namespaced/(?P<pk>\d+)/$', dummy_pk_view, name='namespaced'),
|
url(r'^namespaced/(?P<pk>\d+)/$', dummy_pk_view, name='namespaced'),
|
||||||
]
|
]
|
||||||
|
@ -361,7 +340,7 @@ class TestHyperlinkedRelatedField(URLPatternsTestCase):
|
||||||
self.field.to_internal_value('/v2/namespaced/3/')
|
self.field.to_internal_value('/v2/namespaced/3/')
|
||||||
|
|
||||||
|
|
||||||
class TestNamespaceVersioningHyperlinkedRelatedFieldScheme(URLPatternsTestCase):
|
class TestNamespaceVersioningHyperlinkedRelatedFieldScheme(URLPatternsTestCase, APITestCase):
|
||||||
nested = [
|
nested = [
|
||||||
url(r'^namespaced/(?P<pk>\d+)/$', dummy_pk_view, name='nested'),
|
url(r'^namespaced/(?P<pk>\d+)/$', dummy_pk_view, name='nested'),
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user