Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Added assertFormError, assertTemplateUsed and assertTemplateNotUsed f…

…or use during unit testing.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@5156 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit f073797f4ca8d3e2736d99f76674e43640399c0d 1 parent 64adc41
Russell Keith-Magee authored
60  django/test/testcases.py
@@ -77,4 +77,62 @@ def assertContains(self, response, text, count=1):
77 77
         real_count = response.content.count(text)
78 78
         self.assertEqual(real_count, count,
79 79
             "Could only find %d of %d instances of '%s' in response" % (real_count, count, text))
80  
-            
  80
+
  81
+    def assertFormError(self, response, form, field, errors):
  82
+        "Assert that a form used to render the response has a specific field error"
  83
+        if not response.context:
  84
+            self.fail('Response did not use any contexts to render the response')
  85
+
  86
+        # If there is a single context, put it into a list to simplify processing
  87
+        if not isinstance(response.context, list):
  88
+            contexts = [response.context]
  89
+        else:
  90
+            contexts = response.context
  91
+
  92
+        # If a single error string is provided, make it a list to simplify processing
  93
+        if not isinstance(errors, list):
  94
+            errors = [errors]
  95
+        
  96
+        # Search all contexts for the error.
  97
+        found_form = False
  98
+        for i,context in enumerate(contexts):
  99
+            if form in context:
  100
+                found_form = True
  101
+                try:
  102
+                    for err in errors:
  103
+                        if field:
  104
+                            self.assertTrue(err in context[form].errors[field], 
  105
+                                "The field '%s' on form '%s' in context %d does not contain the error '%s' (actual errors: %s)" % 
  106
+                                    (field, form, i, err, list(context[form].errors[field])))
  107
+                        else:
  108
+                            self.assertTrue(err in context[form].non_field_errors(), 
  109
+                                "The form '%s' in context %d does not contain the non-field error '%s' (actual errors: %s)" % 
  110
+                                    (form, i, err, list(context[form].non_field_errors())))
  111
+                except KeyError:
  112
+                    self.fail("The form '%s' in context %d does not contain the field '%s'" % (form, i, field))
  113
+        if not found_form:
  114
+            self.fail("The form '%s' was not used to render the response" % form)
  115
+            
  116
+    def assertTemplateUsed(self, response, template_name):
  117
+        "Assert that the template with the provided name was used in rendering the response"
  118
+        if isinstance(response.template, list):
  119
+            template_names = [t.name for t in response.template]
  120
+            self.assertTrue(template_name in template_names,
  121
+                "Template '%s' was not one of the templates used to render the response. Templates used: %s" %
  122
+                    (template_name, template_names))
  123
+        elif response.template:
  124
+            self.assertEqual(template_name, response.template.name,
  125
+                "Template '%s' was not used to render the response. Actual template was '%s'" %
  126
+                    (template_name, response.template.name))
  127
+        else:
  128
+            self.fail('No templates used to render the response')
  129
+
  130
+    def assertTemplateNotUsed(self, response, template_name):
  131
+        "Assert that the template with the provided name was NOT used in rendering the response"
  132
+        if isinstance(response.template, list):            
  133
+            self.assertFalse(template_name in [t.name for t in response.template],
  134
+                "Template '%s' was used unexpectedly in rendering the response" % template_name)
  135
+        elif response.template:
  136
+            self.assertNotEqual(template_name, response.template.name,
  137
+                "Template '%s' was used unexpectedly in rendering the response" % template_name)
  138
+        
31  docs/testing.txt
@@ -462,15 +462,36 @@ Normal Python unit tests have a wide range of assertions, such as
462 462
 ``django.TestCase`` adds to these, providing some assertions
463 463
 that can be useful in testing the behavior of web sites.
464 464
 
465  
-``assertRedirects(response, expected_path)``
466  
-    Assert that the response received redirects the browser to the provided
467  
-    path, and that the expected_path can be retrieved.
468  
-
469 465
 ``assertContains(response, text, count=1)``
470  
-    Assert that a response indicates that a page was retreived successfully,
  466
+    Assert that a response indicates that a page was retrieved successfully,
471 467
     (i.e., the HTTP status code was 200), and that ``text`` occurs ``count``
472 468
     times in the content of the response.
473 469
 
  470
+``assertFormError(response, form, field, errors)``
  471
+    Assert that a field on a form raised the provided list of errors when 
  472
+    rendered on the form. 
  473
+    
  474
+    ``form`` is the name the form object was given in the template context. 
  475
+    
  476
+    ``field`` is the name of the field on the form to check. If ``field`` 
  477
+    has a value of ``None``, non-field errors will be checked.
  478
+    
  479
+    ``errors`` is an error string, or a list of error strings, that are 
  480
+    expected as a result of form validation.    
  481
+    
  482
+``assertTemplateNotUsed(response, template_name)``
  483
+    Assert that the template with the given name was *not* used in rendering 
  484
+    the response.
  485
+    
  486
+``assertRedirects(response, expected_path)``
  487
+    Assert that the response received redirects the browser to the provided
  488
+    path, and that the expected_path can be retrieved. 
  489
+
  490
+``assertTemplateUsed(response, template_name)``
  491
+    Assert that the template with the given name was used in rendering the
  492
+    response.
  493
+    
  494
+    
474 495
 Running tests
475 496
 =============
476 497
 
74  tests/modeltests/test_client/models.py
@@ -33,6 +33,13 @@ def test_get_view(self):
33 33
         self.assertEqual(response.context['var'], 42)
34 34
         self.assertEqual(response.template.name, 'GET Template')
35 35
 
  36
+    def test_no_template_view(self):
  37
+        "Check that template usage assersions work then templates aren't in use"
  38
+        response = self.client.get('/test_client/no_template_view/')
  39
+
  40
+        # Check that the no template case doesn't mess with the template assertions
  41
+        self.assertTemplateNotUsed(response, 'GET Template')
  42
+        
36 43
     def test_get_post_view(self):
37 44
         "GET a view that normally expects POSTs"
38 45
         response = self.client.get('/test_client/post_view/', {})
@@ -40,6 +47,8 @@ def test_get_post_view(self):
40 47
         # Check some response details
41 48
         self.assertEqual(response.status_code, 200)
42 49
         self.assertEqual(response.template.name, 'Empty GET Template')
  50
+        self.assertTemplateUsed(response, 'Empty GET Template')
  51
+        self.assertTemplateNotUsed(response, 'Empty POST Template')
43 52
         
44 53
     def test_empty_post(self):
45 54
         "POST an empty dictionary to a view"
@@ -48,6 +57,8 @@ def test_empty_post(self):
48 57
         # Check some response details
49 58
         self.assertEqual(response.status_code, 200)
50 59
         self.assertEqual(response.template.name, 'Empty POST Template')
  60
+        self.assertTemplateNotUsed(response, 'Empty GET Template')
  61
+        self.assertTemplateUsed(response, 'Empty POST Template')
51 62
         
52 63
     def test_post(self):
53 64
         "POST some data to a view"
@@ -88,7 +99,7 @@ def test_valid_form(self):
88 99
         }
89 100
         response = self.client.post('/test_client/form_view/', post_data)
90 101
         self.assertEqual(response.status_code, 200)
91  
-        self.assertEqual(response.template.name, "Valid POST Template")
  102
+        self.assertTemplateUsed(response, "Valid POST Template")
92 103
 
93 104
     def test_incomplete_data_form(self):
94 105
         "POST incomplete data to a form"
@@ -97,8 +108,13 @@ def test_incomplete_data_form(self):
97 108
             'value': 37            
98 109
         }
99 110
         response = self.client.post('/test_client/form_view/', post_data)
100  
-        self.assertContains(response, 'This field is required', 3)
101  
-        self.assertEqual(response.template.name, "Invalid POST Template")
  111
+        self.assertContains(response, 'This field is required.', 3)
  112
+        self.assertEqual(response.status_code, 200)
  113
+        self.assertTemplateUsed(response, "Invalid POST Template")
  114
+
  115
+        self.assertFormError(response, 'form', 'email', 'This field is required.')
  116
+        self.assertFormError(response, 'form', 'single', 'This field is required.')
  117
+        self.assertFormError(response, 'form', 'multi', 'This field is required.')
102 118
 
103 119
     def test_form_error(self):
104 120
         "POST erroneous data to a form"
@@ -111,7 +127,57 @@ def test_form_error(self):
111 127
         }
112 128
         response = self.client.post('/test_client/form_view/', post_data)
113 129
         self.assertEqual(response.status_code, 200)
114  
-        self.assertEqual(response.template.name, "Invalid POST Template")
  130
+        self.assertTemplateUsed(response, "Invalid POST Template")
  131
+
  132
+        self.assertFormError(response, 'form', 'email', 'Enter a valid e-mail address.')
  133
+
  134
+    def test_valid_form_with_template(self):
  135
+        "POST valid data to a form using multiple templates"
  136
+        post_data = {
  137
+            'text': 'Hello World',
  138
+            'email': 'foo@example.com',
  139
+            'value': 37,
  140
+            'single': 'b',
  141
+            'multi': ('b','c','e')
  142
+        }
  143
+        response = self.client.post('/test_client/form_view_with_template/', post_data)
  144
+        self.assertContains(response, 'POST data OK')
  145
+        self.assertTemplateUsed(response, "form_view.html")
  146
+        self.assertTemplateUsed(response, 'base.html')
  147
+        self.assertTemplateNotUsed(response, "Valid POST Template")
  148
+
  149
+    def test_incomplete_data_form_with_template(self):
  150
+        "POST incomplete data to a form using multiple templates"
  151
+        post_data = {
  152
+            'text': 'Hello World',
  153
+            'value': 37            
  154
+        }
  155
+        response = self.client.post('/test_client/form_view_with_template/', post_data)
  156
+        self.assertContains(response, 'POST data has errors')
  157
+        self.assertTemplateUsed(response, 'form_view.html')
  158
+        self.assertTemplateUsed(response, 'base.html')
  159
+        self.assertTemplateNotUsed(response, "Invalid POST Template")
  160
+
  161
+        self.assertFormError(response, 'form', 'email', 'This field is required.')
  162
+        self.assertFormError(response, 'form', 'single', 'This field is required.')
  163
+        self.assertFormError(response, 'form', 'multi', 'This field is required.')
  164
+
  165
+    def test_form_error_with_template(self):
  166
+        "POST erroneous data to a form using multiple templates"
  167
+        post_data = {
  168
+            'text': 'Hello World',
  169
+            'email': 'not an email address',
  170
+            'value': 37,
  171
+            'single': 'b',
  172
+            'multi': ('b','c','e')
  173
+        }
  174
+        response = self.client.post('/test_client/form_view_with_template/', post_data)
  175
+        self.assertContains(response, 'POST data has errors')
  176
+        self.assertTemplateUsed(response, "form_view.html")
  177
+        self.assertTemplateUsed(response, 'base.html')
  178
+        self.assertTemplateNotUsed(response, "Invalid POST Template")
  179
+
  180
+        self.assertFormError(response, 'form', 'email', 'Enter a valid e-mail address.')
115 181
         
116 182
     def test_unknown_page(self):
117 183
         "GET an invalid URL"
2  tests/modeltests/test_client/urls.py
@@ -2,11 +2,13 @@
2 2
 import views
3 3
 
4 4
 urlpatterns = patterns('',
  5
+    (r'^no_template_view/$', views.no_template_view),
5 6
     (r'^get_view/$', views.get_view),
6 7
     (r'^post_view/$', views.post_view),
7 8
     (r'^raw_post_view/$', views.raw_post_view),
8 9
     (r'^redirect_view/$', views.redirect_view),
9 10
     (r'^form_view/$', views.form_view),
  11
+    (r'^form_view_with_template/$', views.form_view_with_template),
10 12
     (r'^login_protected_view/$', views.login_protected_view),
11 13
     (r'^session_view/$', views.session_view),
12 14
     (r'^broken_view/$', views.broken_view)
24  tests/modeltests/test_client/views.py
@@ -4,6 +4,11 @@
4 4
 from django.contrib.auth.decorators import login_required
5 5
 from django.newforms.forms import Form
6 6
 from django.newforms import fields
  7
+from django.shortcuts import render_to_response
  8
+
  9
+def no_template_view(request):
  10
+    "A simple view that expects a GET request, and returns a rendered template"
  11
+    return HttpResponse("No template used")
7 12
 
8 13
 def get_view(request):
9 14
     "A simple view that expects a GET request, and returns a rendered template"
@@ -79,6 +84,25 @@ def form_view(request):
79 84
         c = Context({'form': form})
80 85
     
81 86
     return HttpResponse(t.render(c))
  87
+
  88
+def form_view_with_template(request):
  89
+    "A view that tests a simple form"
  90
+    if request.method == 'POST':
  91
+        form = TestForm(request.POST)
  92
+        if form.is_valid():
  93
+            message = 'POST data OK'
  94
+        else:
  95
+            message = 'POST data has errors'
  96
+    else:
  97
+        form = TestForm()
  98
+        message = 'GET form page'
  99
+    return render_to_response('form_view.html', 
  100
+        { 
  101
+            'form': form,
  102
+            'message': message
  103
+        }
  104
+    )
  105
+
82 106
         
83 107
 def login_protected_view(request):
84 108
     "A simple view that is login protected."
8  tests/templates/base.html
... ...
@@ -0,0 +1,8 @@
  1
+<html>
  2
+<head></head>
  3
+<body>
  4
+<h1>Django Internal Tests: {% block title %}{% endblock %}</h1>
  5
+{% block content %}
  6
+{% endblock %}
  7
+</body>
  8
+</html>
15  tests/templates/form_view.html
... ...
@@ -0,0 +1,15 @@
  1
+{% extends "base.html" %}
  2
+{% block title %}Submit data{% endblock %}
  3
+{% block content %}
  4
+<h1>{{ message }}</h1>
  5
+<form method='post' action='.'>
  6
+{% if form.errors %}
  7
+<p class='warning'>Please correct the errors below:</p>
  8
+{% endif %}
  9
+<ul class='form'>
  10
+{{ form }}
  11
+<li><input type='submit' value='Submit'></li>
  12
+</ul>
  13
+</form>
  14
+
  15
+{% endblock %}
10  tests/templates/login.html
... ...
@@ -1,7 +1,6 @@
1  
-<html>
2  
-<head></head>
3  
-<body>
4  
-<h1>Django Internal Tests: Login</h1>
  1
+{% extends "base.html" %}
  2
+{% block title %}Login{% endblock %}
  3
+{% block content %}
5 4
 {% if form.has_errors %}
6 5
 <p>Your username and password didn't match. Please try again.</p>
7 6
 {% endif %}
@@ -15,5 +14,4 @@
15 14
 <input type="submit" value="login" />
16 15
 <input type="hidden" name="next" value="{{ next }}" />
17 16
 </form>
18  
-</body>
19  
-</html>
  17
+{% endblock %}

0 notes on commit f073797

Please sign in to comment.
Something went wrong with that request. Please try again.