From 12702bc683273b84624aa8353513ff4962a37d35 Mon Sep 17 00:00:00 2001 From: Boulder Sprinters Date: Thu, 10 May 2007 15:51:59 +0000 Subject: [PATCH] boulder-oracle-sprint: Merged to [5182] git-svn-id: http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint@5183 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/test/simple.py | 9 +- django/test/testcases.py | 50 ++++--- docs/testing.txt | 13 +- tests/modeltests/test_client/models.py | 30 +++- tests/modeltests/test_client/urls.py | 5 +- tests/modeltests/test_client/views.py | 14 +- .../test_client_regress/__init__.py | 0 .../test_client_regress/models.py | 136 ++++++++++++++++++ .../test_client_regress/urls.py | 7 + .../test_client_regress/views.py | 8 ++ tests/urls.py | 1 + 11 files changed, 229 insertions(+), 44 deletions(-) create mode 100644 tests/regressiontests/test_client_regress/__init__.py create mode 100644 tests/regressiontests/test_client_regress/models.py create mode 100644 tests/regressiontests/test_client_regress/urls.py create mode 100644 tests/regressiontests/test_client_regress/views.py diff --git a/django/test/simple.py b/django/test/simple.py index fed6237929..cfaa09a0a4 100644 --- a/django/test/simple.py +++ b/django/test/simple.py @@ -49,9 +49,12 @@ def build_suite(app_module): pass else: # The module exists, so there must be an import error in the - # test module itself. We don't need the module; close the file - # handle returned by find_module. - mod[0].close() + # test module itself. We don't need the module; so if the + # module was a single file module (i.e., tests.py), close the file + # handle returned by find_module. Otherwise, the test module + # is a directory, and there is nothing to close. + if mod[0]: + mod[0].close() raise return suite diff --git a/django/test/testcases.py b/django/test/testcases.py index 153931f42a..788e215ca1 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -56,32 +56,35 @@ class TestCase(unittest.TestCase): self._pre_setup() super(TestCase, self).run(result) - def assertRedirects(self, response, expected_path): - """Assert that a response redirected to a specific URL, and that the + def assertRedirects(self, response, expected_path, status_code=302, target_status_code=200): + """Assert that a response redirected to a specific URL, and that the redirect URL can be loaded. """ - self.assertEqual(response.status_code, 302, - "Response didn't redirect: Reponse code was %d" % response.status_code) + self.assertEqual(response.status_code, status_code, + "Response didn't redirect: Reponse code was %d (expected %d)" % + (response.status_code, status_code)) scheme, netloc, path, params, query, fragment = urlparse(response['Location']) self.assertEqual(path, expected_path, "Response redirected to '%s', expected '%s'" % (path, expected_path)) redirect_response = self.client.get(path) - self.assertEqual(redirect_response.status_code, 200, - "Couldn't retrieve redirection page '%s'" % path) + self.assertEqual(redirect_response.status_code, target_status_code, + "Couldn't retrieve redirection page '%s': response code was %d (expected %d)" % + (path, response.status_code, status_code)) - def assertContains(self, response, text, count=1): + def assertContains(self, response, text, count=1, status_code=200): """Assert that a response indicates that a page was retreived successfully, - (i.e., the HTTP status code was 200), and that ``text`` occurs ``count`` + (i.e., the HTTP status code was as expected), and that ``text`` occurs ``count`` times in the content of the response. """ - self.assertEqual(response.status_code, 200, - "Couldn't retrieve page'") + self.assertEqual(response.status_code, status_code, + "Couldn't retrieve page: Response code was %d (expected %d)'" % + (response.status_code, status_code)) real_count = response.content.count(text) self.assertEqual(real_count, count, - "Could only find %d of %d instances of '%s' in response" % (real_count, count, text)) - + "Found %d instances of '%s' in response (expected %d)" % (real_count, text, count)) + 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: @@ -102,18 +105,21 @@ class TestCase(unittest.TestCase): for i,context in enumerate(contexts): if form in context: found_form = True - try: - for err in errors: - if field: + for err in errors: + if field: + if field in context[form].errors: 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]))) + "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]))) + elif field in context[form].fields: + self.fail("The field '%s' on form '%s' in context %d contains no errors" % + (field, form, i)) 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)) + self.fail("The form '%s' in context %d does not contain the field '%s'" % (form, i, 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()))) if not found_form: self.fail("The form '%s' was not used to render the response" % form) diff --git a/docs/testing.txt b/docs/testing.txt index ba13dab67e..b44b67c20a 100644 --- a/docs/testing.txt +++ b/docs/testing.txt @@ -472,9 +472,9 @@ Normal Python unit tests have a wide range of assertions, such as ``django.TestCase`` adds to these, providing some assertions 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`` +``assertContains(response, text, count=1, status_code=200)`` + Assert that a response indicates that a page could be retrieved and + produced the nominated status code, and that ``text`` occurs ``count`` times in the content of the response. ``assertFormError(response, form, field, errors)`` @@ -493,9 +493,10 @@ that can be useful in testing the behavior of web sites. Assert that the template with the given name was *not* used in rendering the response. -``assertRedirects(response, expected_path)`` - Assert that the response received redirects the browser to the provided - path, and that the expected_path can be retrieved. +``assertRedirects(response, expected_path, status_code=302, target_status_code=200)`` + Assert that the response received produced the nominated status code, + redirects the browser to the provided path, and that retrieving the provided + path yields a response with the target status code. ``assertTemplateUsed(response, template_name)`` Assert that the template with the given name was used in rendering the diff --git a/tests/modeltests/test_client/models.py b/tests/modeltests/test_client/models.py index 34242ee0d8..5ebf29678c 100644 --- a/tests/modeltests/test_client/models.py +++ b/tests/modeltests/test_client/models.py @@ -34,13 +34,6 @@ class ClientTest(TestCase): self.assertEqual(response.context['var'], 42) 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): "GET a view that normally expects POSTs" response = self.client.get('/test_client/post_view/', {}) @@ -75,6 +68,7 @@ class ClientTest(TestCase): self.failUnless('Data received' in response.content) def test_raw_post(self): + "POST raw data (with a content type) to a view" test_doc = """BlinkMalcolm Gladwell""" response = self.client.post("/test_client/raw_post_view/", test_doc, content_type="text/xml") @@ -89,6 +83,28 @@ class ClientTest(TestCase): # Check that the response was a 302 (redirect) self.assertRedirects(response, '/test_client/get_view/') + 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) + self.assertRedirects(response, '/test_client/get_view/', status_code=301) + + 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 + self.assertRedirects(response, '/test_client/permanent_redirect_view/', target_status_code=301) + + 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) + def test_valid_form(self): "POST valid data to a form" post_data = { diff --git a/tests/modeltests/test_client/urls.py b/tests/modeltests/test_client/urls.py index 52fc8fe692..538c0e4b43 100644 --- a/tests/modeltests/test_client/urls.py +++ b/tests/modeltests/test_client/urls.py @@ -1,12 +1,15 @@ from django.conf.urls.defaults import * +from django.views.generic.simple import redirect_to import views urlpatterns = patterns('', - (r'^no_template_view/$', views.no_template_view), (r'^get_view/$', views.get_view), (r'^post_view/$', views.post_view), (r'^raw_post_view/$', views.raw_post_view), (r'^redirect_view/$', views.redirect_view), + (r'^permanent_redirect_view/$', redirect_to, { 'url': '/test_client/get_view/' }), + (r'^double_redirect_view/$', views.double_redirect_view), + (r'^bad_view/$', views.bad_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), diff --git a/tests/modeltests/test_client/views.py b/tests/modeltests/test_client/views.py index 18d6a2dcd9..9bdf213b35 100644 --- a/tests/modeltests/test_client/views.py +++ b/tests/modeltests/test_client/views.py @@ -1,16 +1,12 @@ from xml.dom.minidom import parseString from django.core.mail import EmailMessage, SMTPConnection from django.template import Context, Template -from django.http import HttpResponse, HttpResponseRedirect +from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound from django.contrib.auth.decorators import login_required from django.newforms.forms import Form 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): "A simple view that expects a GET request, and returns a rendered template" t = Template('This is a test. {{ var }} is the value.', name='GET Template') @@ -54,6 +50,14 @@ def redirect_view(request): "A view that redirects all requests to the GET view" return HttpResponseRedirect('/test_client/get_view/') +def double_redirect_view(request): + "A view that redirects all requests to a redirection view" + return HttpResponseRedirect('/test_client/permanent_redirect_view/') + +def bad_view(request): + "A view that returns a 404 with some error content" + return HttpResponseNotFound('Not found!. This page contains some MAGIC content') + TestChoices = ( ('a', 'First Choice'), ('b', 'Second Choice'), diff --git a/tests/regressiontests/test_client_regress/__init__.py b/tests/regressiontests/test_client_regress/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py new file mode 100644 index 0000000000..c39fafe314 --- /dev/null +++ b/tests/regressiontests/test_client_regress/models.py @@ -0,0 +1,136 @@ +""" +Regression tests for the Test Client, especially the customized assertions. + +""" +from django.test import Client, TestCase +from django.core import mail + +class AssertTemplateUsedTests(TestCase): + fixtures = ['testdata.json'] + + def test_no_context(self): + "Template usage assertions work then templates aren't in use" + response = self.client.get('/test_client_regress/no_template_view/') + + # Check that the no template case doesn't mess with the template assertions + self.assertTemplateNotUsed(response, 'GET Template') + + try: + self.assertTemplateUsed(response, 'GET Template') + except AssertionError, e: + self.assertEquals(str(e), "No templates used to render the response") + + def test_single_context(self): + "Template assertions work when there is a single context" + response = self.client.get('/test_client/post_view/', {}) + + # + try: + self.assertTemplateNotUsed(response, 'Empty GET Template') + except AssertionError, e: + self.assertEquals(str(e), "Template 'Empty GET Template' was used unexpectedly in rendering the response") + + try: + self.assertTemplateUsed(response, 'Empty POST Template') + except AssertionError, e: + self.assertEquals(str(e), "Template 'Empty POST Template' was not used to render the response. Actual template was 'Empty GET Template'") + + def test_multiple_context(self): + "Template assertions work when there are multiple contexts" + 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') + try: + self.assertTemplateNotUsed(response, "form_view.html") + except AssertionError, e: + self.assertEquals(str(e), "Template 'form_view.html' was used unexpectedly in rendering the response") + + try: + self.assertTemplateNotUsed(response, 'base.html') + except AssertionError, e: + self.assertEquals(str(e), "Template 'base.html' was used unexpectedly in rendering the response") + + try: + self.assertTemplateUsed(response, "Valid POST Template") + except AssertionError, e: + self.assertEquals(str(e), "Template 'Valid POST Template' was not one of the templates used to render the response. Templates used: ['form_view.html', 'base.html']") + +class AssertFormErrorTests(TestCase): + def test_unknown_form(self): + "An assertion is raised if the form name is unknown" + 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) + self.assertTemplateUsed(response, "Invalid POST Template") + + try: + self.assertFormError(response, 'wrong_form', 'some_field', 'Some error.') + except AssertionError, e: + self.assertEqual(str(e), "The form 'wrong_form' was not used to render the response") + + def test_unknown_field(self): + "An assertion is raised if the field name is unknown" + 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) + self.assertTemplateUsed(response, "Invalid POST Template") + + try: + self.assertFormError(response, 'form', 'some_field', 'Some error.') + except AssertionError, e: + self.assertEqual(str(e), "The form 'form' in context 0 does not contain the field 'some_field'") + + def test_noerror_field(self): + "An assertion is raised if the field doesn't have any errors" + 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) + self.assertTemplateUsed(response, "Invalid POST Template") + + try: + self.assertFormError(response, 'form', 'value', 'Some error.') + except AssertionError, e: + self.assertEqual(str(e), "The field 'value' on form 'form' in context 0 contains no errors") + + def test_unknown_error(self): + "An assertion is raised if the field doesn't contain the provided error" + 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) + self.assertTemplateUsed(response, "Invalid POST Template") + + try: + self.assertFormError(response, 'form', 'email', 'Some error.') + except AssertionError, e: + self.assertEqual(str(e), "The field 'email' on form 'form' in context 0 does not contain the error 'Some error.' (actual errors: [u'Enter a valid e-mail address.'])") + diff --git a/tests/regressiontests/test_client_regress/urls.py b/tests/regressiontests/test_client_regress/urls.py new file mode 100644 index 0000000000..e9cd0aea15 --- /dev/null +++ b/tests/regressiontests/test_client_regress/urls.py @@ -0,0 +1,7 @@ +from django.conf.urls.defaults import * +from django.views.generic.simple import redirect_to +import views + +urlpatterns = patterns('', + (r'^no_template_view/$', views.no_template_view), +) diff --git a/tests/regressiontests/test_client_regress/views.py b/tests/regressiontests/test_client_regress/views.py new file mode 100644 index 0000000000..d8dd2b349c --- /dev/null +++ b/tests/regressiontests/test_client_regress/views.py @@ -0,0 +1,8 @@ +from django.core.mail import EmailMessage, SMTPConnection +from django.http import HttpResponse +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") + diff --git a/tests/urls.py b/tests/urls.py index 9dcdca944e..dd475b0ea7 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -3,6 +3,7 @@ from django.conf.urls.defaults import * urlpatterns = patterns('', # test_client modeltest urls (r'^test_client/', include('modeltests.test_client.urls')), + (r'^test_client_regress/', include('regressiontests.test_client_regress.urls')), # Always provide the auth system login and logout views (r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}),