Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 12:11:04 +00:00
|
|
|
# coding: utf-8
|
2006-09-02 09:34:40 +00:00
|
|
|
"""
|
2007-03-23 20:17:04 +00:00
|
|
|
38. Testing using the Test Client
|
2006-09-02 09:34:40 +00:00
|
|
|
|
|
|
|
The test client is a class that can act like a simple
|
|
|
|
browser for testing purposes.
|
|
|
|
|
|
|
|
It allows the user to compose GET and POST requests, and
|
|
|
|
obtain the response that the server gave to those requests.
|
|
|
|
The server Response objects are annotated with the details
|
|
|
|
of the contexts and templates that were rendered during the
|
|
|
|
process of serving the request.
|
|
|
|
|
|
|
|
Client objects are stateful - they will retain cookie (and
|
|
|
|
thus session) details for the lifetime of the Client instance.
|
|
|
|
|
|
|
|
This is not intended as a replacement for Twill,Selenium, or
|
|
|
|
other browser automation frameworks - it is here to allow
|
|
|
|
testing against the contexts and templates produced by a view,
|
|
|
|
rather than the HTML rendered to the end-user.
|
|
|
|
|
|
|
|
"""
|
2007-03-01 13:11:08 +00:00
|
|
|
from django.test import Client, TestCase
|
2007-05-08 11:19:34 +00:00
|
|
|
from django.core import mail
|
2006-09-02 09:34:40 +00:00
|
|
|
|
2007-03-01 13:11:08 +00:00
|
|
|
class ClientTest(TestCase):
|
|
|
|
fixtures = ['testdata.json']
|
|
|
|
|
2006-09-02 09:34:40 +00:00
|
|
|
def test_get_view(self):
|
|
|
|
"GET a view"
|
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 12:11:04 +00:00
|
|
|
# The data is ignored, but let's check it doesn't crash the system
|
|
|
|
# anyway.
|
|
|
|
data = {'var': u'\xf2'}
|
|
|
|
response = self.client.get('/test_client/get_view/', data)
|
2006-09-02 09:34:40 +00:00
|
|
|
|
|
|
|
# Check some response details
|
2007-05-05 03:03:33 +00:00
|
|
|
self.assertContains(response, 'This is a test')
|
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 12:11:04 +00:00
|
|
|
self.assertEqual(response.context['var'], u'\xf2')
|
2006-09-02 09:34:40 +00:00
|
|
|
self.assertEqual(response.template.name, 'GET Template')
|
|
|
|
|
|
|
|
def test_get_post_view(self):
|
|
|
|
"GET a view that normally expects POSTs"
|
|
|
|
response = self.client.get('/test_client/post_view/', {})
|
|
|
|
|
|
|
|
# Check some response details
|
|
|
|
self.assertEqual(response.status_code, 200)
|
2007-02-17 00:23:09 +00:00
|
|
|
self.assertEqual(response.template.name, 'Empty GET Template')
|
2007-05-07 12:34:18 +00:00
|
|
|
self.assertTemplateUsed(response, 'Empty GET Template')
|
|
|
|
self.assertTemplateNotUsed(response, 'Empty POST Template')
|
2006-09-02 09:34:40 +00:00
|
|
|
|
|
|
|
def test_empty_post(self):
|
|
|
|
"POST an empty dictionary to a view"
|
|
|
|
response = self.client.post('/test_client/post_view/', {})
|
|
|
|
|
|
|
|
# Check some response details
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
self.assertEqual(response.template.name, 'Empty POST Template')
|
2007-05-07 12:34:18 +00:00
|
|
|
self.assertTemplateNotUsed(response, 'Empty GET Template')
|
|
|
|
self.assertTemplateUsed(response, 'Empty POST Template')
|
2006-09-02 09:34:40 +00:00
|
|
|
|
2007-02-17 00:23:09 +00:00
|
|
|
def test_post(self):
|
2006-09-02 09:34:40 +00:00
|
|
|
"POST some data to a view"
|
|
|
|
post_data = {
|
|
|
|
'value': 37
|
|
|
|
}
|
|
|
|
response = self.client.post('/test_client/post_view/', post_data)
|
|
|
|
|
|
|
|
# Check some response details
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
self.assertEqual(response.context['data'], '37')
|
|
|
|
self.assertEqual(response.template.name, 'POST Template')
|
|
|
|
self.failUnless('Data received' in response.content)
|
|
|
|
|
2007-02-17 00:23:09 +00:00
|
|
|
def test_raw_post(self):
|
2007-05-10 11:27:59 +00:00
|
|
|
"POST raw data (with a content type) to a view"
|
2007-02-17 00:23:09 +00:00
|
|
|
test_doc = """<?xml version="1.0" encoding="utf-8"?><library><book><title>Blink</title><author>Malcolm Gladwell</author></book></library>"""
|
|
|
|
response = self.client.post("/test_client/raw_post_view/", test_doc,
|
|
|
|
content_type="text/xml")
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
self.assertEqual(response.template.name, "Book template")
|
|
|
|
self.assertEqual(response.content, "Blink - Malcolm Gladwell")
|
|
|
|
|
2006-09-02 09:34:40 +00:00
|
|
|
def test_redirect(self):
|
|
|
|
"GET a URL that redirects elsewhere"
|
|
|
|
response = self.client.get('/test_client/redirect_view/')
|
|
|
|
# Check that the response was a 302 (redirect)
|
2007-09-14 06:05:54 +00:00
|
|
|
self.assertRedirects(response, 'http://testserver/test_client/get_view/')
|
2007-09-14 05:28:00 +00:00
|
|
|
|
|
|
|
client_providing_host = Client(HTTP_HOST='django.testserver')
|
|
|
|
response = client_providing_host.get('/test_client/redirect_view/')
|
|
|
|
# Check that the response was a 302 (redirect) with absolute URI
|
|
|
|
self.assertRedirects(response, 'http://django.testserver/test_client/get_view/')
|
2007-08-31 11:37:28 +00:00
|
|
|
|
|
|
|
def test_redirect_with_query(self):
|
|
|
|
"GET a URL that redirects with given GET parameters"
|
|
|
|
response = self.client.get('/test_client/redirect_view/', {'var': 'value'})
|
|
|
|
|
|
|
|
# Check if parameters are intact
|
2007-09-14 06:05:54 +00:00
|
|
|
self.assertRedirects(response, 'http://testserver/test_client/get_view/?var=value')
|
2007-03-22 10:23:52 +00:00
|
|
|
|
2007-05-10 11:27:59 +00:00
|
|
|
def test_permanent_redirect(self):
|
|
|
|
"GET a URL that redirects permanently elsewhere"
|
|
|
|
response = self.client.get('/test_client/permanent_redirect_view/')
|
|
|
|
# Check that the response was a 301 (permanent redirect)
|
2007-09-14 06:05:54 +00:00
|
|
|
self.assertRedirects(response, 'http://testserver/test_client/get_view/', status_code=301)
|
2007-05-10 11:27:59 +00:00
|
|
|
|
2007-09-14 05:28:00 +00:00
|
|
|
client_providing_host = Client(HTTP_HOST='django.testserver')
|
|
|
|
response = client_providing_host.get('/test_client/permanent_redirect_view/')
|
|
|
|
# Check that the response was a 301 (permanent redirect) with absolute URI
|
|
|
|
self.assertRedirects(response, 'http://django.testserver/test_client/get_view/', status_code=301)
|
|
|
|
|
2007-05-10 11:27:59 +00:00
|
|
|
def test_redirect_to_strange_location(self):
|
|
|
|
"GET a URL that redirects to a non-200 page"
|
|
|
|
response = self.client.get('/test_client/double_redirect_view/')
|
|
|
|
|
|
|
|
# Check that the response was a 302, and that
|
|
|
|
# the attempt to get the redirection location returned 301 when retrieved
|
2007-09-14 06:05:54 +00:00
|
|
|
self.assertRedirects(response, 'http://testserver/test_client/permanent_redirect_view/', target_status_code=301)
|
2007-05-10 11:27:59 +00:00
|
|
|
|
|
|
|
def test_notfound_response(self):
|
|
|
|
"GET a URL that responds as '404:Not Found'"
|
|
|
|
response = self.client.get('/test_client/bad_view/')
|
|
|
|
|
|
|
|
# Check that the response was a 404, and that the content contains MAGIC
|
|
|
|
self.assertContains(response, 'MAGIC', status_code=404)
|
|
|
|
|
2007-03-22 10:23:52 +00:00
|
|
|
def test_valid_form(self):
|
|
|
|
"POST valid data to a form"
|
|
|
|
post_data = {
|
|
|
|
'text': 'Hello World',
|
|
|
|
'email': 'foo@example.com',
|
|
|
|
'value': 37,
|
|
|
|
'single': 'b',
|
|
|
|
'multi': ('b','c','e')
|
|
|
|
}
|
|
|
|
response = self.client.post('/test_client/form_view/', post_data)
|
|
|
|
self.assertEqual(response.status_code, 200)
|
2007-05-07 12:34:18 +00:00
|
|
|
self.assertTemplateUsed(response, "Valid POST Template")
|
2007-03-22 10:23:52 +00:00
|
|
|
|
2007-07-21 05:17:20 +00:00
|
|
|
def test_valid_form_with_hints(self):
|
|
|
|
"GET a form, providing hints in the GET data"
|
|
|
|
hints = {
|
|
|
|
'text': 'Hello World',
|
|
|
|
'multi': ('b','c','e')
|
|
|
|
}
|
|
|
|
response = self.client.get('/test_client/form_view/', data=hints)
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
self.assertTemplateUsed(response, "Form GET Template")
|
|
|
|
# Check that the multi-value data has been rolled out ok
|
|
|
|
self.assertContains(response, 'Select a valid choice.', 0)
|
|
|
|
|
2007-03-22 10:23:52 +00:00
|
|
|
def test_incomplete_data_form(self):
|
|
|
|
"POST incomplete data to a form"
|
|
|
|
post_data = {
|
|
|
|
'text': 'Hello World',
|
|
|
|
'value': 37
|
|
|
|
}
|
|
|
|
response = self.client.post('/test_client/form_view/', post_data)
|
2007-05-07 12:34:18 +00:00
|
|
|
self.assertContains(response, 'This field is required.', 3)
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
self.assertTemplateUsed(response, "Invalid POST Template")
|
|
|
|
|
|
|
|
self.assertFormError(response, 'form', 'email', 'This field is required.')
|
|
|
|
self.assertFormError(response, 'form', 'single', 'This field is required.')
|
|
|
|
self.assertFormError(response, 'form', 'multi', 'This field is required.')
|
2007-03-22 10:23:52 +00:00
|
|
|
|
|
|
|
def test_form_error(self):
|
|
|
|
"POST erroneous data to a form"
|
|
|
|
post_data = {
|
|
|
|
'text': 'Hello World',
|
|
|
|
'email': 'not an email address',
|
|
|
|
'value': 37,
|
|
|
|
'single': 'b',
|
|
|
|
'multi': ('b','c','e')
|
|
|
|
}
|
|
|
|
response = self.client.post('/test_client/form_view/', post_data)
|
|
|
|
self.assertEqual(response.status_code, 200)
|
2007-05-07 12:34:18 +00:00
|
|
|
self.assertTemplateUsed(response, "Invalid POST Template")
|
|
|
|
|
|
|
|
self.assertFormError(response, 'form', 'email', 'Enter a valid e-mail address.')
|
|
|
|
|
|
|
|
def test_valid_form_with_template(self):
|
|
|
|
"POST valid data to a form using multiple templates"
|
|
|
|
post_data = {
|
|
|
|
'text': 'Hello World',
|
|
|
|
'email': 'foo@example.com',
|
|
|
|
'value': 37,
|
|
|
|
'single': 'b',
|
|
|
|
'multi': ('b','c','e')
|
|
|
|
}
|
|
|
|
response = self.client.post('/test_client/form_view_with_template/', post_data)
|
|
|
|
self.assertContains(response, 'POST data OK')
|
|
|
|
self.assertTemplateUsed(response, "form_view.html")
|
|
|
|
self.assertTemplateUsed(response, 'base.html')
|
|
|
|
self.assertTemplateNotUsed(response, "Valid POST Template")
|
|
|
|
|
|
|
|
def test_incomplete_data_form_with_template(self):
|
|
|
|
"POST incomplete data to a form using multiple templates"
|
|
|
|
post_data = {
|
|
|
|
'text': 'Hello World',
|
|
|
|
'value': 37
|
|
|
|
}
|
|
|
|
response = self.client.post('/test_client/form_view_with_template/', post_data)
|
|
|
|
self.assertContains(response, 'POST data has errors')
|
|
|
|
self.assertTemplateUsed(response, 'form_view.html')
|
|
|
|
self.assertTemplateUsed(response, 'base.html')
|
|
|
|
self.assertTemplateNotUsed(response, "Invalid POST Template")
|
|
|
|
|
|
|
|
self.assertFormError(response, 'form', 'email', 'This field is required.')
|
|
|
|
self.assertFormError(response, 'form', 'single', 'This field is required.')
|
|
|
|
self.assertFormError(response, 'form', 'multi', 'This field is required.')
|
|
|
|
|
|
|
|
def test_form_error_with_template(self):
|
|
|
|
"POST erroneous data to a form using multiple templates"
|
|
|
|
post_data = {
|
|
|
|
'text': 'Hello World',
|
|
|
|
'email': 'not an email address',
|
|
|
|
'value': 37,
|
|
|
|
'single': 'b',
|
|
|
|
'multi': ('b','c','e')
|
|
|
|
}
|
|
|
|
response = self.client.post('/test_client/form_view_with_template/', post_data)
|
|
|
|
self.assertContains(response, 'POST data has errors')
|
|
|
|
self.assertTemplateUsed(response, "form_view.html")
|
|
|
|
self.assertTemplateUsed(response, 'base.html')
|
|
|
|
self.assertTemplateNotUsed(response, "Invalid POST Template")
|
|
|
|
|
|
|
|
self.assertFormError(response, 'form', 'email', 'Enter a valid e-mail address.')
|
2007-03-22 10:23:52 +00:00
|
|
|
|
2006-09-02 09:34:40 +00:00
|
|
|
def test_unknown_page(self):
|
|
|
|
"GET an invalid URL"
|
|
|
|
response = self.client.get('/test_client/unknown_view/')
|
|
|
|
|
|
|
|
# Check that the response was a 404
|
|
|
|
self.assertEqual(response.status_code, 404)
|
|
|
|
|
|
|
|
def test_view_with_login(self):
|
|
|
|
"Request a page that is protected with @login_required"
|
|
|
|
|
|
|
|
# Get the page without logging in. Should result in 302.
|
|
|
|
response = self.client.get('/test_client/login_protected_view/')
|
2007-09-14 06:05:54 +00:00
|
|
|
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/login_protected_view/')
|
2006-09-02 09:34:40 +00:00
|
|
|
|
2007-05-05 15:16:15 +00:00
|
|
|
# Log in
|
2007-09-03 11:21:40 +00:00
|
|
|
login = self.client.login(username='testclient', password='password')
|
|
|
|
self.assertTrue(login, 'Could not log in')
|
2007-05-05 15:16:15 +00:00
|
|
|
|
2006-09-02 09:34:40 +00:00
|
|
|
# Request a page that requires a login
|
2007-05-05 15:16:15 +00:00
|
|
|
response = self.client.get('/test_client/login_protected_view/')
|
2006-09-02 09:34:40 +00:00
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
self.assertEqual(response.context['user'].username, 'testclient')
|
|
|
|
|
|
|
|
def test_view_with_bad_login(self):
|
|
|
|
"Request a page that is protected with @login, but use bad credentials"
|
|
|
|
|
2007-05-05 15:16:15 +00:00
|
|
|
login = self.client.login(username='otheruser', password='nopassword')
|
|
|
|
self.failIf(login)
|
2007-02-09 13:47:36 +00:00
|
|
|
|
2007-07-12 15:26:37 +00:00
|
|
|
def test_view_with_inactive_login(self):
|
|
|
|
"Request a page that is protected with @login, but use an inactive login"
|
|
|
|
|
|
|
|
login = self.client.login(username='inactive', password='password')
|
|
|
|
self.failIf(login)
|
|
|
|
|
2007-08-17 14:20:25 +00:00
|
|
|
def test_logout(self):
|
2007-08-17 14:42:25 +00:00
|
|
|
"Request a logout after logging in"
|
2007-08-17 14:20:25 +00:00
|
|
|
# Log in
|
|
|
|
self.client.login(username='testclient', password='password')
|
|
|
|
|
|
|
|
# Request a page that requires a login
|
|
|
|
response = self.client.get('/test_client/login_protected_view/')
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
self.assertEqual(response.context['user'].username, 'testclient')
|
|
|
|
|
|
|
|
# Log out
|
|
|
|
self.client.logout()
|
|
|
|
|
|
|
|
# Request a page that requires a login
|
|
|
|
response = self.client.get('/test_client/login_protected_view/')
|
2007-09-14 06:05:54 +00:00
|
|
|
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/login_protected_view/')
|
2007-08-17 14:20:25 +00:00
|
|
|
|
2007-02-09 13:47:36 +00:00
|
|
|
def test_session_modifying_view(self):
|
|
|
|
"Request a page that modifies the session"
|
|
|
|
# Session value isn't set initially
|
|
|
|
try:
|
|
|
|
self.client.session['tobacconist']
|
|
|
|
self.fail("Shouldn't have a session value")
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
from django.contrib.sessions.models import Session
|
|
|
|
response = self.client.post('/test_client/session_view/')
|
|
|
|
|
|
|
|
# Check that the session was modified
|
|
|
|
self.assertEquals(self.client.session['tobacconist'], 'hovercraft')
|
2007-02-11 00:23:31 +00:00
|
|
|
|
|
|
|
def test_view_with_exception(self):
|
|
|
|
"Request a page that is known to throw an error"
|
|
|
|
self.assertRaises(KeyError, self.client.get, "/test_client/broken_view/")
|
|
|
|
|
|
|
|
#Try the same assertion, a different way
|
|
|
|
try:
|
|
|
|
self.client.get('/test_client/broken_view/')
|
|
|
|
self.fail('Should raise an error')
|
|
|
|
except KeyError:
|
|
|
|
pass
|
2007-05-08 11:19:34 +00:00
|
|
|
|
|
|
|
def test_mail_sending(self):
|
|
|
|
"Test that mail is redirected to a dummy outbox during test setup"
|
|
|
|
|
|
|
|
response = self.client.get('/test_client/mail_sending_view/')
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
self.assertEqual(len(mail.outbox), 1)
|
|
|
|
self.assertEqual(mail.outbox[0].subject, 'Test message')
|
|
|
|
self.assertEqual(mail.outbox[0].body, 'This is a test email')
|
|
|
|
self.assertEqual(mail.outbox[0].from_email, 'from@example.com')
|
|
|
|
self.assertEqual(mail.outbox[0].to[0], 'first@example.com')
|
|
|
|
self.assertEqual(mail.outbox[0].to[1], 'second@example.com')
|
|
|
|
|
|
|
|
def test_mass_mail_sending(self):
|
|
|
|
"Test that mass mail is redirected to a dummy outbox during test setup"
|
|
|
|
|
|
|
|
response = self.client.get('/test_client/mass_mail_sending_view/')
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
self.assertEqual(len(mail.outbox), 2)
|
|
|
|
self.assertEqual(mail.outbox[0].subject, 'First Test message')
|
|
|
|
self.assertEqual(mail.outbox[0].body, 'This is the first test email')
|
|
|
|
self.assertEqual(mail.outbox[0].from_email, 'from@example.com')
|
|
|
|
self.assertEqual(mail.outbox[0].to[0], 'first@example.com')
|
|
|
|
self.assertEqual(mail.outbox[0].to[1], 'second@example.com')
|
|
|
|
|
|
|
|
self.assertEqual(mail.outbox[1].subject, 'Second Test message')
|
|
|
|
self.assertEqual(mail.outbox[1].body, 'This is the second test email')
|
|
|
|
self.assertEqual(mail.outbox[1].from_email, 'from@example.com')
|
|
|
|
self.assertEqual(mail.outbox[1].to[0], 'second@example.com')
|
|
|
|
self.assertEqual(mail.outbox[1].to[1], 'third@example.com')
|
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 12:11:04 +00:00
|
|
|
|