/tests/modeltests/test_client/models.py
Python | 490 lines | 448 code | 14 blank | 28 comment | 3 complexity | 442cf751ad572ccad3288b2983571428 MD5 | raw file
1# coding: utf-8 2""" 339. Testing using the Test Client 4 5The test client is a class that can act like a simple 6browser for testing purposes. 7 8It allows the user to compose GET and POST requests, and 9obtain the response that the server gave to those requests. 10The server Response objects are annotated with the details 11of the contexts and templates that were rendered during the 12process of serving the request. 13 14``Client`` objects are stateful - they will retain cookie (and 15thus session) details for the lifetime of the ``Client`` instance. 16 17This is not intended as a replacement for Twill, Selenium, or 18other browser automation frameworks - it is here to allow 19testing against the contexts and templates produced by a view, 20rather than the HTML rendered to the end-user. 21 22""" 23from django.conf import settings 24from django.core import mail 25from django.test import Client, TestCase, RequestFactory 26 27from views import get_view 28 29 30class ClientTest(TestCase): 31 fixtures = ['testdata.json'] 32 33 def test_get_view(self): 34 "GET a view" 35 # The data is ignored, but let's check it doesn't crash the system 36 # anyway. 37 data = {'var': u'\xf2'} 38 response = self.client.get('/test_client/get_view/', data) 39 40 # Check some response details 41 self.assertContains(response, 'This is a test') 42 self.assertEqual(response.context['var'], u'\xf2') 43 self.assertEqual(response.templates[0].name, 'GET Template') 44 45 def test_get_post_view(self): 46 "GET a view that normally expects POSTs" 47 response = self.client.get('/test_client/post_view/', {}) 48 49 # Check some response details 50 self.assertEqual(response.status_code, 200) 51 self.assertEqual(response.templates[0].name, 'Empty GET Template') 52 self.assertTemplateUsed(response, 'Empty GET Template') 53 self.assertTemplateNotUsed(response, 'Empty POST Template') 54 55 def test_empty_post(self): 56 "POST an empty dictionary to a view" 57 response = self.client.post('/test_client/post_view/', {}) 58 59 # Check some response details 60 self.assertEqual(response.status_code, 200) 61 self.assertEqual(response.templates[0].name, 'Empty POST Template') 62 self.assertTemplateNotUsed(response, 'Empty GET Template') 63 self.assertTemplateUsed(response, 'Empty POST Template') 64 65 def test_post(self): 66 "POST some data to a view" 67 post_data = { 68 'value': 37 69 } 70 response = self.client.post('/test_client/post_view/', post_data) 71 72 # Check some response details 73 self.assertEqual(response.status_code, 200) 74 self.assertEqual(response.context['data'], '37') 75 self.assertEqual(response.templates[0].name, 'POST Template') 76 self.assertTrue('Data received' in response.content) 77 78 def test_response_headers(self): 79 "Check the value of HTTP headers returned in a response" 80 response = self.client.get("/test_client/header_view/") 81 82 self.assertEqual(response['X-DJANGO-TEST'], 'Slartibartfast') 83 84 def test_raw_post(self): 85 "POST raw data (with a content type) to a view" 86 test_doc = """<?xml version="1.0" encoding="utf-8"?><library><book><title>Blink</title><author>Malcolm Gladwell</author></book></library>""" 87 response = self.client.post("/test_client/raw_post_view/", test_doc, 88 content_type="text/xml") 89 self.assertEqual(response.status_code, 200) 90 self.assertEqual(response.templates[0].name, "Book template") 91 self.assertEqual(response.content, "Blink - Malcolm Gladwell") 92 93 def test_redirect(self): 94 "GET a URL that redirects elsewhere" 95 response = self.client.get('/test_client/redirect_view/') 96 # Check that the response was a 302 (redirect) and that 97 # assertRedirect() understands to put an implicit http://testserver/ in 98 # front of non-absolute URLs. 99 self.assertRedirects(response, '/test_client/get_view/') 100 101 host = 'django.testserver' 102 client_providing_host = Client(HTTP_HOST=host) 103 response = client_providing_host.get('/test_client/redirect_view/') 104 # Check that the response was a 302 (redirect) with absolute URI 105 self.assertRedirects(response, '/test_client/get_view/', host=host) 106 107 def test_redirect_with_query(self): 108 "GET a URL that redirects with given GET parameters" 109 response = self.client.get('/test_client/redirect_view/', {'var': 'value'}) 110 111 # Check if parameters are intact 112 self.assertRedirects(response, 'http://testserver/test_client/get_view/?var=value') 113 114 def test_permanent_redirect(self): 115 "GET a URL that redirects permanently elsewhere" 116 response = self.client.get('/test_client/permanent_redirect_view/') 117 # Check that the response was a 301 (permanent redirect) 118 self.assertRedirects(response, 'http://testserver/test_client/get_view/', status_code=301) 119 120 client_providing_host = Client(HTTP_HOST='django.testserver') 121 response = client_providing_host.get('/test_client/permanent_redirect_view/') 122 # Check that the response was a 301 (permanent redirect) with absolute URI 123 self.assertRedirects(response, 'http://django.testserver/test_client/get_view/', status_code=301) 124 125 def test_temporary_redirect(self): 126 "GET a URL that does a non-permanent redirect" 127 response = self.client.get('/test_client/temporary_redirect_view/') 128 # Check that the response was a 302 (non-permanent redirect) 129 self.assertRedirects(response, 'http://testserver/test_client/get_view/', status_code=302) 130 131 def test_redirect_to_strange_location(self): 132 "GET a URL that redirects to a non-200 page" 133 response = self.client.get('/test_client/double_redirect_view/') 134 135 # Check that the response was a 302, and that 136 # the attempt to get the redirection location returned 301 when retrieved 137 self.assertRedirects(response, 'http://testserver/test_client/permanent_redirect_view/', target_status_code=301) 138 139 def test_follow_redirect(self): 140 "A URL that redirects can be followed to termination." 141 response = self.client.get('/test_client/double_redirect_view/', follow=True) 142 self.assertRedirects(response, 'http://testserver/test_client/get_view/', status_code=302, target_status_code=200) 143 self.assertEqual(len(response.redirect_chain), 2) 144 145 def test_redirect_http(self): 146 "GET a URL that redirects to an http URI" 147 response = self.client.get('/test_client/http_redirect_view/',follow=True) 148 self.assertFalse(response.test_was_secure_request) 149 150 def test_redirect_https(self): 151 "GET a URL that redirects to an https URI" 152 response = self.client.get('/test_client/https_redirect_view/',follow=True) 153 self.assertTrue(response.test_was_secure_request) 154 155 def test_notfound_response(self): 156 "GET a URL that responds as '404:Not Found'" 157 response = self.client.get('/test_client/bad_view/') 158 159 # Check that the response was a 404, and that the content contains MAGIC 160 self.assertContains(response, 'MAGIC', status_code=404) 161 162 def test_valid_form(self): 163 "POST valid data to a form" 164 post_data = { 165 'text': 'Hello World', 166 'email': 'foo@example.com', 167 'value': 37, 168 'single': 'b', 169 'multi': ('b','c','e') 170 } 171 response = self.client.post('/test_client/form_view/', post_data) 172 self.assertEqual(response.status_code, 200) 173 self.assertTemplateUsed(response, "Valid POST Template") 174 175 def test_valid_form_with_hints(self): 176 "GET a form, providing hints in the GET data" 177 hints = { 178 'text': 'Hello World', 179 'multi': ('b','c','e') 180 } 181 response = self.client.get('/test_client/form_view/', data=hints) 182 self.assertEqual(response.status_code, 200) 183 self.assertTemplateUsed(response, "Form GET Template") 184 # Check that the multi-value data has been rolled out ok 185 self.assertContains(response, 'Select a valid choice.', 0) 186 187 def test_incomplete_data_form(self): 188 "POST incomplete data to a form" 189 post_data = { 190 'text': 'Hello World', 191 'value': 37 192 } 193 response = self.client.post('/test_client/form_view/', post_data) 194 self.assertContains(response, 'This field is required.', 3) 195 self.assertEqual(response.status_code, 200) 196 self.assertTemplateUsed(response, "Invalid POST Template") 197 198 self.assertFormError(response, 'form', 'email', 'This field is required.') 199 self.assertFormError(response, 'form', 'single', 'This field is required.') 200 self.assertFormError(response, 'form', 'multi', 'This field is required.') 201 202 def test_form_error(self): 203 "POST erroneous data to a form" 204 post_data = { 205 'text': 'Hello World', 206 'email': 'not an email address', 207 'value': 37, 208 'single': 'b', 209 'multi': ('b','c','e') 210 } 211 response = self.client.post('/test_client/form_view/', post_data) 212 self.assertEqual(response.status_code, 200) 213 self.assertTemplateUsed(response, "Invalid POST Template") 214 215 self.assertFormError(response, 'form', 'email', 'Enter a valid e-mail address.') 216 217 def test_valid_form_with_template(self): 218 "POST valid data to a form using multiple templates" 219 post_data = { 220 'text': 'Hello World', 221 'email': 'foo@example.com', 222 'value': 37, 223 'single': 'b', 224 'multi': ('b','c','e') 225 } 226 response = self.client.post('/test_client/form_view_with_template/', post_data) 227 self.assertContains(response, 'POST data OK') 228 self.assertTemplateUsed(response, "form_view.html") 229 self.assertTemplateUsed(response, 'base.html') 230 self.assertTemplateNotUsed(response, "Valid POST Template") 231 232 def test_incomplete_data_form_with_template(self): 233 "POST incomplete data to a form using multiple templates" 234 post_data = { 235 'text': 'Hello World', 236 'value': 37 237 } 238 response = self.client.post('/test_client/form_view_with_template/', post_data) 239 self.assertContains(response, 'POST data has errors') 240 self.assertTemplateUsed(response, 'form_view.html') 241 self.assertTemplateUsed(response, 'base.html') 242 self.assertTemplateNotUsed(response, "Invalid POST Template") 243 244 self.assertFormError(response, 'form', 'email', 'This field is required.') 245 self.assertFormError(response, 'form', 'single', 'This field is required.') 246 self.assertFormError(response, 'form', 'multi', 'This field is required.') 247 248 def test_form_error_with_template(self): 249 "POST erroneous data to a form using multiple templates" 250 post_data = { 251 'text': 'Hello World', 252 'email': 'not an email address', 253 'value': 37, 254 'single': 'b', 255 'multi': ('b','c','e') 256 } 257 response = self.client.post('/test_client/form_view_with_template/', post_data) 258 self.assertContains(response, 'POST data has errors') 259 self.assertTemplateUsed(response, "form_view.html") 260 self.assertTemplateUsed(response, 'base.html') 261 self.assertTemplateNotUsed(response, "Invalid POST Template") 262 263 self.assertFormError(response, 'form', 'email', 'Enter a valid e-mail address.') 264 265 def test_unknown_page(self): 266 "GET an invalid URL" 267 response = self.client.get('/test_client/unknown_view/') 268 269 # Check that the response was a 404 270 self.assertEqual(response.status_code, 404) 271 272 def test_url_parameters(self): 273 "Make sure that URL ;-parameters are not stripped." 274 response = self.client.get('/test_client/unknown_view/;some-parameter') 275 276 # Check that the path in the response includes it (ignore that it's a 404) 277 self.assertEqual(response.request['PATH_INFO'], '/test_client/unknown_view/;some-parameter') 278 279 def test_view_with_login(self): 280 "Request a page that is protected with @login_required" 281 282 # Get the page without logging in. Should result in 302. 283 response = self.client.get('/test_client/login_protected_view/') 284 self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/login_protected_view/') 285 286 # Log in 287 login = self.client.login(username='testclient', password='password') 288 self.assertTrue(login, 'Could not log in') 289 290 # Request a page that requires a login 291 response = self.client.get('/test_client/login_protected_view/') 292 self.assertEqual(response.status_code, 200) 293 self.assertEqual(response.context['user'].username, 'testclient') 294 295 def test_view_with_method_login(self): 296 "Request a page that is protected with a @login_required method" 297 298 # Get the page without logging in. Should result in 302. 299 response = self.client.get('/test_client/login_protected_method_view/') 300 self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/login_protected_method_view/') 301 302 # Log in 303 login = self.client.login(username='testclient', password='password') 304 self.assertTrue(login, 'Could not log in') 305 306 # Request a page that requires a login 307 response = self.client.get('/test_client/login_protected_method_view/') 308 self.assertEqual(response.status_code, 200) 309 self.assertEqual(response.context['user'].username, 'testclient') 310 311 def test_view_with_login_and_custom_redirect(self): 312 "Request a page that is protected with @login_required(redirect_field_name='redirect_to')" 313 314 # Get the page without logging in. Should result in 302. 315 response = self.client.get('/test_client/login_protected_view_custom_redirect/') 316 self.assertRedirects(response, 'http://testserver/accounts/login/?redirect_to=/test_client/login_protected_view_custom_redirect/') 317 318 # Log in 319 login = self.client.login(username='testclient', password='password') 320 self.assertTrue(login, 'Could not log in') 321 322 # Request a page that requires a login 323 response = self.client.get('/test_client/login_protected_view_custom_redirect/') 324 self.assertEqual(response.status_code, 200) 325 self.assertEqual(response.context['user'].username, 'testclient') 326 327 def test_view_with_bad_login(self): 328 "Request a page that is protected with @login, but use bad credentials" 329 330 login = self.client.login(username='otheruser', password='nopassword') 331 self.assertFalse(login) 332 333 def test_view_with_inactive_login(self): 334 "Request a page that is protected with @login, but use an inactive login" 335 336 login = self.client.login(username='inactive', password='password') 337 self.assertFalse(login) 338 339 def test_logout(self): 340 "Request a logout after logging in" 341 # Log in 342 self.client.login(username='testclient', password='password') 343 344 # Request a page that requires a login 345 response = self.client.get('/test_client/login_protected_view/') 346 self.assertEqual(response.status_code, 200) 347 self.assertEqual(response.context['user'].username, 'testclient') 348 349 # Log out 350 self.client.logout() 351 352 # Request a page that requires a login 353 response = self.client.get('/test_client/login_protected_view/') 354 self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/login_protected_view/') 355 356 def test_view_with_permissions(self): 357 "Request a page that is protected with @permission_required" 358 359 # Get the page without logging in. Should result in 302. 360 response = self.client.get('/test_client/permission_protected_view/') 361 self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/permission_protected_view/') 362 363 # Log in 364 login = self.client.login(username='testclient', password='password') 365 self.assertTrue(login, 'Could not log in') 366 367 # Log in with wrong permissions. Should result in 302. 368 response = self.client.get('/test_client/permission_protected_view/') 369 self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/permission_protected_view/') 370 371 # TODO: Log in with right permissions and request the page again 372 373 def test_view_with_method_permissions(self): 374 "Request a page that is protected with a @permission_required method" 375 376 # Get the page without logging in. Should result in 302. 377 response = self.client.get('/test_client/permission_protected_method_view/') 378 self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/permission_protected_method_view/') 379 380 # Log in 381 login = self.client.login(username='testclient', password='password') 382 self.assertTrue(login, 'Could not log in') 383 384 # Log in with wrong permissions. Should result in 302. 385 response = self.client.get('/test_client/permission_protected_method_view/') 386 self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/permission_protected_method_view/') 387 388 # TODO: Log in with right permissions and request the page again 389 390 def test_session_modifying_view(self): 391 "Request a page that modifies the session" 392 # Session value isn't set initially 393 try: 394 self.client.session['tobacconist'] 395 self.fail("Shouldn't have a session value") 396 except KeyError: 397 pass 398 399 from django.contrib.sessions.models import Session 400 response = self.client.post('/test_client/session_view/') 401 402 # Check that the session was modified 403 self.assertEqual(self.client.session['tobacconist'], 'hovercraft') 404 405 def test_view_with_exception(self): 406 "Request a page that is known to throw an error" 407 self.assertRaises(KeyError, self.client.get, "/test_client/broken_view/") 408 409 #Try the same assertion, a different way 410 try: 411 self.client.get('/test_client/broken_view/') 412 self.fail('Should raise an error') 413 except KeyError: 414 pass 415 416 def test_mail_sending(self): 417 "Test that mail is redirected to a dummy outbox during test setup" 418 419 response = self.client.get('/test_client/mail_sending_view/') 420 self.assertEqual(response.status_code, 200) 421 422 self.assertEqual(len(mail.outbox), 1) 423 self.assertEqual(mail.outbox[0].subject, 'Test message') 424 self.assertEqual(mail.outbox[0].body, 'This is a test email') 425 self.assertEqual(mail.outbox[0].from_email, 'from@example.com') 426 self.assertEqual(mail.outbox[0].to[0], 'first@example.com') 427 self.assertEqual(mail.outbox[0].to[1], 'second@example.com') 428 429 def test_mass_mail_sending(self): 430 "Test that mass mail is redirected to a dummy outbox during test setup" 431 432 response = self.client.get('/test_client/mass_mail_sending_view/') 433 self.assertEqual(response.status_code, 200) 434 435 self.assertEqual(len(mail.outbox), 2) 436 self.assertEqual(mail.outbox[0].subject, 'First Test message') 437 self.assertEqual(mail.outbox[0].body, 'This is the first test email') 438 self.assertEqual(mail.outbox[0].from_email, 'from@example.com') 439 self.assertEqual(mail.outbox[0].to[0], 'first@example.com') 440 self.assertEqual(mail.outbox[0].to[1], 'second@example.com') 441 442 self.assertEqual(mail.outbox[1].subject, 'Second Test message') 443 self.assertEqual(mail.outbox[1].body, 'This is the second test email') 444 self.assertEqual(mail.outbox[1].from_email, 'from@example.com') 445 self.assertEqual(mail.outbox[1].to[0], 'second@example.com') 446 self.assertEqual(mail.outbox[1].to[1], 'third@example.com') 447 448class CSRFEnabledClientTests(TestCase): 449 def setUp(self): 450 # Enable the CSRF middleware for this test 451 self.old_MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES 452 csrf_middleware_class = 'django.middleware.csrf.CsrfViewMiddleware' 453 if csrf_middleware_class not in settings.MIDDLEWARE_CLASSES: 454 settings.MIDDLEWARE_CLASSES += (csrf_middleware_class,) 455 456 def tearDown(self): 457 settings.MIDDLEWARE_CLASSES = self.old_MIDDLEWARE_CLASSES 458 459 def test_csrf_enabled_client(self): 460 "A client can be instantiated with CSRF checks enabled" 461 csrf_client = Client(enforce_csrf_checks=True) 462 463 # The normal client allows the post 464 response = self.client.post('/test_client/post_view/', {}) 465 self.assertEqual(response.status_code, 200) 466 467 # The CSRF-enabled client rejects it 468 response = csrf_client.post('/test_client/post_view/', {}) 469 self.assertEqual(response.status_code, 403) 470 471 472class CustomTestClient(Client): 473 i_am_customized = "Yes" 474 475class CustomTestClientTest(TestCase): 476 client_class = CustomTestClient 477 478 def test_custom_test_client(self): 479 """A test case can specify a custom class for self.client.""" 480 self.assertEqual(hasattr(self.client, "i_am_customized"), True) 481 482 483class RequestFactoryTest(TestCase): 484 def test_request_factory(self): 485 factory = RequestFactory() 486 request = factory.get('/somewhere/') 487 response = get_view(request) 488 489 self.assertEqual(response.status_code, 200) 490 self.assertContains(response, 'This is a test')