/tests/regressiontests/generic_views/base.py
Python | 293 lines | 257 code | 24 blank | 12 comment | 3 complexity | 74ba569ae5c2065a24761afea8485036 MD5 | raw file
Possible License(s): BSD-3-Clause
1import time 2import unittest 3 4from django.core.exceptions import ImproperlyConfigured 5from django.http import HttpResponse 6from django.test import TestCase, RequestFactory 7from django.utils import simplejson 8from django.views.generic import View, TemplateView, RedirectView 9 10 11class SimpleView(View): 12 """ 13 A simple view with a docstring. 14 """ 15 def get(self, request): 16 return HttpResponse('This is a simple view') 17 18 19class SimplePostView(SimpleView): 20 post = SimpleView.get 21 22 23class CustomizableView(SimpleView): 24 parameter = {} 25 26def decorator(view): 27 view.is_decorated = True 28 return view 29 30 31class DecoratedDispatchView(SimpleView): 32 33 @decorator 34 def dispatch(self, request, *args, **kwargs): 35 return super(DecoratedDispatchView, self).dispatch(request, *args, **kwargs) 36 37 38class AboutTemplateView(TemplateView): 39 def get(self, request): 40 return self.render_to_response({}) 41 42 def get_template_names(self): 43 return ['generic_views/about.html'] 44 45 46class AboutTemplateAttributeView(TemplateView): 47 template_name = 'generic_views/about.html' 48 49 def get(self, request): 50 return self.render_to_response(context={}) 51 52 53class InstanceView(View): 54 55 def get(self, request): 56 return self 57 58 59class ViewTest(unittest.TestCase): 60 rf = RequestFactory() 61 62 def _assert_simple(self, response): 63 self.assertEqual(response.status_code, 200) 64 self.assertEqual(response.content, 'This is a simple view') 65 66 def test_no_init_kwargs(self): 67 """ 68 Test that a view can't be accidentally instantiated before deployment 69 """ 70 try: 71 view = SimpleView(key='value').as_view() 72 self.fail('Should not be able to instantiate a view') 73 except AttributeError: 74 pass 75 76 def test_no_init_args(self): 77 """ 78 Test that a view can't be accidentally instantiated before deployment 79 """ 80 try: 81 view = SimpleView.as_view('value') 82 self.fail('Should not be able to use non-keyword arguments instantiating a view') 83 except TypeError: 84 pass 85 86 def test_pathological_http_method(self): 87 """ 88 The edge case of a http request that spoofs an existing method name is caught. 89 """ 90 self.assertEqual(SimpleView.as_view()( 91 self.rf.get('/', REQUEST_METHOD='DISPATCH') 92 ).status_code, 405) 93 94 def test_get_only(self): 95 """ 96 Test a view which only allows GET doesn't allow other methods. 97 """ 98 self._assert_simple(SimpleView.as_view()(self.rf.get('/'))) 99 self.assertEqual(SimpleView.as_view()(self.rf.post('/')).status_code, 405) 100 self.assertEqual(SimpleView.as_view()( 101 self.rf.get('/', REQUEST_METHOD='FAKE') 102 ).status_code, 405) 103 104 def test_get_and_post(self): 105 """ 106 Test a view which only allows both GET and POST. 107 """ 108 self._assert_simple(SimplePostView.as_view()(self.rf.get('/'))) 109 self._assert_simple(SimplePostView.as_view()(self.rf.post('/'))) 110 self.assertEqual(SimplePostView.as_view()( 111 self.rf.get('/', REQUEST_METHOD='FAKE') 112 ).status_code, 405) 113 114 def test_invalid_keyword_argument(self): 115 """ 116 Test that view arguments must be predefined on the class and can't 117 be named like a HTTP method. 118 """ 119 # Check each of the allowed method names 120 for method in SimpleView.http_method_names: 121 kwargs = dict(((method, "value"),)) 122 self.assertRaises(TypeError, SimpleView.as_view, **kwargs) 123 124 # Check the case view argument is ok if predefined on the class... 125 CustomizableView.as_view(parameter="value") 126 # ...but raises errors otherwise. 127 self.assertRaises(TypeError, CustomizableView.as_view, foobar="value") 128 129 def test_calling_more_than_once(self): 130 """ 131 Test a view can only be called once. 132 """ 133 request = self.rf.get('/') 134 view = InstanceView.as_view() 135 self.assertNotEqual(view(request), view(request)) 136 137 def test_class_attributes(self): 138 """ 139 Test that the callable returned from as_view() has proper 140 docstring, name and module. 141 """ 142 self.assertEqual(SimpleView.__doc__, SimpleView.as_view().__doc__) 143 self.assertEqual(SimpleView.__name__, SimpleView.as_view().__name__) 144 self.assertEqual(SimpleView.__module__, SimpleView.as_view().__module__) 145 146 def test_dispatch_decoration(self): 147 """ 148 Test that attributes set by decorators on the dispatch method 149 are also present on the closure. 150 """ 151 self.assertTrue(DecoratedDispatchView.as_view().is_decorated) 152 153 154class TemplateViewTest(TestCase): 155 urls = 'regressiontests.generic_views.urls' 156 157 rf = RequestFactory() 158 159 def _assert_about(self, response): 160 response.render() 161 self.assertEqual(response.status_code, 200) 162 self.assertContains(response, '<h1>About</h1>') 163 164 def test_get(self): 165 """ 166 Test a view that simply renders a template on GET 167 """ 168 self._assert_about(AboutTemplateView.as_view()(self.rf.get('/about/'))) 169 170 def test_get_template_attribute(self): 171 """ 172 Test a view that renders a template on GET with the template name as 173 an attribute on the class. 174 """ 175 self._assert_about(AboutTemplateAttributeView.as_view()(self.rf.get('/about/'))) 176 177 def test_get_generic_template(self): 178 """ 179 Test a completely generic view that renders a template on GET 180 with the template name as an argument at instantiation. 181 """ 182 self._assert_about(TemplateView.as_view(template_name='generic_views/about.html')(self.rf.get('/about/'))) 183 184 def test_template_name_required(self): 185 """ 186 A template view must provide a template name 187 """ 188 self.assertRaises(ImproperlyConfigured, self.client.get, '/template/no_template/') 189 190 def test_template_params(self): 191 """ 192 A generic template view passes kwargs as context. 193 """ 194 response = self.client.get('/template/simple/bar/') 195 self.assertEqual(response.status_code, 200) 196 self.assertEqual(response.context['params'], {'foo': 'bar'}) 197 198 def test_extra_template_params(self): 199 """ 200 A template view can be customized to return extra context. 201 """ 202 response = self.client.get('/template/custom/bar/') 203 self.assertEqual(response.status_code, 200) 204 self.assertEqual(response.context['params'], {'foo': 'bar'}) 205 self.assertEqual(response.context['key'], 'value') 206 207 def test_cached_views(self): 208 """ 209 A template view can be cached 210 """ 211 response = self.client.get('/template/cached/bar/') 212 self.assertEqual(response.status_code, 200) 213 214 time.sleep(1.0) 215 216 response2 = self.client.get('/template/cached/bar/') 217 self.assertEqual(response2.status_code, 200) 218 219 self.assertEqual(response.content, response2.content) 220 221 time.sleep(2.0) 222 223 # Let the cache expire and test again 224 response2 = self.client.get('/template/cached/bar/') 225 self.assertEqual(response2.status_code, 200) 226 227 self.assertNotEqual(response.content, response2.content) 228 229class RedirectViewTest(unittest.TestCase): 230 rf = RequestFactory() 231 232 def test_no_url(self): 233 "Without any configuration, returns HTTP 410 GONE" 234 response = RedirectView.as_view()(self.rf.get('/foo/')) 235 self.assertEqual(response.status_code, 410) 236 237 def test_permanaent_redirect(self): 238 "Default is a permanent redirect" 239 response = RedirectView.as_view(url='/bar/')(self.rf.get('/foo/')) 240 self.assertEqual(response.status_code, 301) 241 self.assertEqual(response['Location'], '/bar/') 242 243 def test_temporary_redirect(self): 244 "Permanent redirects are an option" 245 response = RedirectView.as_view(url='/bar/', permanent=False)(self.rf.get('/foo/')) 246 self.assertEqual(response.status_code, 302) 247 self.assertEqual(response['Location'], '/bar/') 248 249 def test_include_args(self): 250 "GET arguments can be included in the redirected URL" 251 response = RedirectView.as_view(url='/bar/')(self.rf.get('/foo/')) 252 self.assertEqual(response.status_code, 301) 253 self.assertEqual(response['Location'], '/bar/') 254 255 response = RedirectView.as_view(url='/bar/', query_string=True)(self.rf.get('/foo/?pork=spam')) 256 self.assertEqual(response.status_code, 301) 257 self.assertEqual(response['Location'], '/bar/?pork=spam') 258 259 def test_parameter_substitution(self): 260 "Redirection URLs can be parameterized" 261 response = RedirectView.as_view(url='/bar/%(object_id)d/')(self.rf.get('/foo/42/'), object_id=42) 262 self.assertEqual(response.status_code, 301) 263 self.assertEqual(response['Location'], '/bar/42/') 264 265 def test_redirect_POST(self): 266 "Default is a permanent redirect" 267 response = RedirectView.as_view(url='/bar/')(self.rf.post('/foo/')) 268 self.assertEqual(response.status_code, 301) 269 self.assertEqual(response['Location'], '/bar/') 270 271 def test_redirect_HEAD(self): 272 "Default is a permanent redirect" 273 response = RedirectView.as_view(url='/bar/')(self.rf.head('/foo/')) 274 self.assertEqual(response.status_code, 301) 275 self.assertEqual(response['Location'], '/bar/') 276 277 def test_redirect_OPTIONS(self): 278 "Default is a permanent redirect" 279 response = RedirectView.as_view(url='/bar/')(self.rf.options('/foo/')) 280 self.assertEqual(response.status_code, 301) 281 self.assertEqual(response['Location'], '/bar/') 282 283 def test_redirect_PUT(self): 284 "Default is a permanent redirect" 285 response = RedirectView.as_view(url='/bar/')(self.rf.put('/foo/')) 286 self.assertEqual(response.status_code, 301) 287 self.assertEqual(response['Location'], '/bar/') 288 289 def test_redirect_DELETE(self): 290 "Default is a permanent redirect" 291 response = RedirectView.as_view(url='/bar/')(self.rf.delete('/foo/')) 292 self.assertEqual(response.status_code, 301) 293 self.assertEqual(response['Location'], '/bar/')