Added assertFormError, assertTemplateUsed and assertTemplateNotUsed for use during unit testing.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@5156 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2007-05-07 12:34:18 +00:00
parent 64adc41377
commit f073797f4c
8 changed files with 208 additions and 16 deletions

View File

@ -78,3 +78,61 @@ class TestCase(unittest.TestCase):
self.assertEqual(real_count, count, self.assertEqual(real_count, count,
"Could only find %d of %d instances of '%s' in response" % (real_count, count, text)) "Could only find %d of %d instances of '%s' in response" % (real_count, count, text))
def assertFormError(self, response, form, field, errors):
"Assert that a form used to render the response has a specific field error"
if not response.context:
self.fail('Response did not use any contexts to render the response')
# If there is a single context, put it into a list to simplify processing
if not isinstance(response.context, list):
contexts = [response.context]
else:
contexts = response.context
# If a single error string is provided, make it a list to simplify processing
if not isinstance(errors, list):
errors = [errors]
# Search all contexts for the error.
found_form = False
for i,context in enumerate(contexts):
if form in context:
found_form = True
try:
for err in errors:
if field:
self.assertTrue(err in context[form].errors[field],
"The field '%s' on form '%s' in context %d does not contain the error '%s' (actual errors: %s)" %
(field, form, i, err, list(context[form].errors[field])))
else:
self.assertTrue(err in context[form].non_field_errors(),
"The form '%s' in context %d does not contain the non-field error '%s' (actual errors: %s)" %
(form, i, err, list(context[form].non_field_errors())))
except KeyError:
self.fail("The form '%s' in context %d does not contain the field '%s'" % (form, i, field))
if not found_form:
self.fail("The form '%s' was not used to render the response" % form)
def assertTemplateUsed(self, response, template_name):
"Assert that the template with the provided name was used in rendering the response"
if isinstance(response.template, list):
template_names = [t.name for t in response.template]
self.assertTrue(template_name in template_names,
"Template '%s' was not one of the templates used to render the response. Templates used: %s" %
(template_name, template_names))
elif response.template:
self.assertEqual(template_name, response.template.name,
"Template '%s' was not used to render the response. Actual template was '%s'" %
(template_name, response.template.name))
else:
self.fail('No templates used to render the response')
def assertTemplateNotUsed(self, response, template_name):
"Assert that the template with the provided name was NOT used in rendering the response"
if isinstance(response.template, list):
self.assertFalse(template_name in [t.name for t in response.template],
"Template '%s' was used unexpectedly in rendering the response" % template_name)
elif response.template:
self.assertNotEqual(template_name, response.template.name,
"Template '%s' was used unexpectedly in rendering the response" % template_name)

View File

@ -462,14 +462,35 @@ Normal Python unit tests have a wide range of assertions, such as
``django.TestCase`` adds to these, providing some assertions ``django.TestCase`` adds to these, providing some assertions
that can be useful in testing the behavior of web sites. that can be useful in testing the behavior of web sites.
``assertContains(response, text, count=1)``
Assert that a response indicates that a page was retrieved successfully,
(i.e., the HTTP status code was 200), and that ``text`` occurs ``count``
times in the content of the response.
``assertFormError(response, form, field, errors)``
Assert that a field on a form raised the provided list of errors when
rendered on the form.
``form`` is the name the form object was given in the template context.
``field`` is the name of the field on the form to check. If ``field``
has a value of ``None``, non-field errors will be checked.
``errors`` is an error string, or a list of error strings, that are
expected as a result of form validation.
``assertTemplateNotUsed(response, template_name)``
Assert that the template with the given name was *not* used in rendering
the response.
``assertRedirects(response, expected_path)`` ``assertRedirects(response, expected_path)``
Assert that the response received redirects the browser to the provided Assert that the response received redirects the browser to the provided
path, and that the expected_path can be retrieved. path, and that the expected_path can be retrieved.
``assertContains(response, text, count=1)`` ``assertTemplateUsed(response, template_name)``
Assert that a response indicates that a page was retreived successfully, Assert that the template with the given name was used in rendering the
(i.e., the HTTP status code was 200), and that ``text`` occurs ``count`` response.
times in the content of the response.
Running tests Running tests
============= =============

View File

@ -33,6 +33,13 @@ class ClientTest(TestCase):
self.assertEqual(response.context['var'], 42) self.assertEqual(response.context['var'], 42)
self.assertEqual(response.template.name, 'GET Template') self.assertEqual(response.template.name, 'GET Template')
def test_no_template_view(self):
"Check that template usage assersions work then templates aren't in use"
response = self.client.get('/test_client/no_template_view/')
# Check that the no template case doesn't mess with the template assertions
self.assertTemplateNotUsed(response, 'GET Template')
def test_get_post_view(self): def test_get_post_view(self):
"GET a view that normally expects POSTs" "GET a view that normally expects POSTs"
response = self.client.get('/test_client/post_view/', {}) response = self.client.get('/test_client/post_view/', {})
@ -40,6 +47,8 @@ class ClientTest(TestCase):
# Check some response details # Check some response details
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.template.name, 'Empty GET Template') self.assertEqual(response.template.name, 'Empty GET Template')
self.assertTemplateUsed(response, 'Empty GET Template')
self.assertTemplateNotUsed(response, 'Empty POST Template')
def test_empty_post(self): def test_empty_post(self):
"POST an empty dictionary to a view" "POST an empty dictionary to a view"
@ -48,6 +57,8 @@ class ClientTest(TestCase):
# Check some response details # Check some response details
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.template.name, 'Empty POST Template') self.assertEqual(response.template.name, 'Empty POST Template')
self.assertTemplateNotUsed(response, 'Empty GET Template')
self.assertTemplateUsed(response, 'Empty POST Template')
def test_post(self): def test_post(self):
"POST some data to a view" "POST some data to a view"
@ -88,7 +99,7 @@ class ClientTest(TestCase):
} }
response = self.client.post('/test_client/form_view/', post_data) response = self.client.post('/test_client/form_view/', post_data)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.template.name, "Valid POST Template") self.assertTemplateUsed(response, "Valid POST Template")
def test_incomplete_data_form(self): def test_incomplete_data_form(self):
"POST incomplete data to a form" "POST incomplete data to a form"
@ -97,8 +108,13 @@ class ClientTest(TestCase):
'value': 37 'value': 37
} }
response = self.client.post('/test_client/form_view/', post_data) response = self.client.post('/test_client/form_view/', post_data)
self.assertContains(response, 'This field is required', 3) self.assertContains(response, 'This field is required.', 3)
self.assertEqual(response.template.name, "Invalid POST Template") 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.')
def test_form_error(self): def test_form_error(self):
"POST erroneous data to a form" "POST erroneous data to a form"
@ -111,7 +127,57 @@ class ClientTest(TestCase):
} }
response = self.client.post('/test_client/form_view/', post_data) response = self.client.post('/test_client/form_view/', post_data)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.template.name, "Invalid POST Template") 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.')
def test_unknown_page(self): def test_unknown_page(self):
"GET an invalid URL" "GET an invalid URL"

View File

@ -2,11 +2,13 @@ from django.conf.urls.defaults import *
import views import views
urlpatterns = patterns('', urlpatterns = patterns('',
(r'^no_template_view/$', views.no_template_view),
(r'^get_view/$', views.get_view), (r'^get_view/$', views.get_view),
(r'^post_view/$', views.post_view), (r'^post_view/$', views.post_view),
(r'^raw_post_view/$', views.raw_post_view), (r'^raw_post_view/$', views.raw_post_view),
(r'^redirect_view/$', views.redirect_view), (r'^redirect_view/$', views.redirect_view),
(r'^form_view/$', views.form_view), (r'^form_view/$', views.form_view),
(r'^form_view_with_template/$', views.form_view_with_template),
(r'^login_protected_view/$', views.login_protected_view), (r'^login_protected_view/$', views.login_protected_view),
(r'^session_view/$', views.session_view), (r'^session_view/$', views.session_view),
(r'^broken_view/$', views.broken_view) (r'^broken_view/$', views.broken_view)

View File

@ -4,6 +4,11 @@ from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.newforms.forms import Form from django.newforms.forms import Form
from django.newforms import fields from django.newforms import fields
from django.shortcuts import render_to_response
def no_template_view(request):
"A simple view that expects a GET request, and returns a rendered template"
return HttpResponse("No template used")
def get_view(request): def get_view(request):
"A simple view that expects a GET request, and returns a rendered template" "A simple view that expects a GET request, and returns a rendered template"
@ -80,6 +85,25 @@ def form_view(request):
return HttpResponse(t.render(c)) return HttpResponse(t.render(c))
def form_view_with_template(request):
"A view that tests a simple form"
if request.method == 'POST':
form = TestForm(request.POST)
if form.is_valid():
message = 'POST data OK'
else:
message = 'POST data has errors'
else:
form = TestForm()
message = 'GET form page'
return render_to_response('form_view.html',
{
'form': form,
'message': message
}
)
def login_protected_view(request): def login_protected_view(request):
"A simple view that is login protected." "A simple view that is login protected."
t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template') t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template')

View File

@ -0,0 +1,8 @@
<html>
<head></head>
<body>
<h1>Django Internal Tests: {% block title %}{% endblock %}</h1>
{% block content %}
{% endblock %}
</body>
</html>

View File

@ -0,0 +1,15 @@
{% extends "base.html" %}
{% block title %}Submit data{% endblock %}
{% block content %}
<h1>{{ message }}</h1>
<form method='post' action='.'>
{% if form.errors %}
<p class='warning'>Please correct the errors below:</p>
{% endif %}
<ul class='form'>
{{ form }}
<li><input type='submit' value='Submit'></li>
</ul>
</form>
{% endblock %}

View File

@ -1,7 +1,6 @@
<html> {% extends "base.html" %}
<head></head> {% block title %}Login{% endblock %}
<body> {% block content %}
<h1>Django Internal Tests: Login</h1>
{% if form.has_errors %} {% if form.has_errors %}
<p>Your username and password didn't match. Please try again.</p> <p>Your username and password didn't match. Please try again.</p>
{% endif %} {% endif %}
@ -15,5 +14,4 @@
<input type="submit" value="login" /> <input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" /> <input type="hidden" name="next" value="{{ next }}" />
</form> </form>
</body> {% endblock %}
</html>