Refs #2333 - Added model test for the test Client.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@3708 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
d043200077
commit
826b9ff5e5
|
@ -0,0 +1,10 @@
|
||||||
|
from django.dispatch import dispatcher
|
||||||
|
from django.db.models import signals
|
||||||
|
import models as test_client_app
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
def setup_test(app, created_models, verbosity):
|
||||||
|
# Create a user account for the login-based tests
|
||||||
|
User.objects.create_user('testclient','testclient@example.com', 'password')
|
||||||
|
|
||||||
|
dispatcher.connect(setup_test, sender=test_client_app, signal=signals.post_syncdb)
|
|
@ -0,0 +1,101 @@
|
||||||
|
"""
|
||||||
|
39. Testing using the Test Client
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.test.client import Client
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class ClientTest(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
"Set up test environment"
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
def test_get_view(self):
|
||||||
|
"GET a view"
|
||||||
|
response = self.client.get('/test_client/get_view/')
|
||||||
|
|
||||||
|
# Check some response details
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.context['var'], 42)
|
||||||
|
self.assertEqual(response.template.name, 'GET Template')
|
||||||
|
self.failUnless('This is a test.' in response.content)
|
||||||
|
|
||||||
|
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)
|
||||||
|
self.assertEqual(response.template.name, 'Empty POST Template')
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
def test_post_view(self):
|
||||||
|
"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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
|
||||||
|
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/')
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
|
||||||
|
# Request a page that requires a login
|
||||||
|
response = self.client.login('/test_client/login_protected_view/', 'testclient', 'password')
|
||||||
|
self.assertTrue(response)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.context['user'].username, 'testclient')
|
||||||
|
self.assertEqual(response.template.name, 'Login Template')
|
||||||
|
|
||||||
|
def test_view_with_bad_login(self):
|
||||||
|
"Request a page that is protected with @login, but use bad credentials"
|
||||||
|
|
||||||
|
response = self.client.login('/test_client/login_protected_view/', 'otheruser', 'nopassword')
|
||||||
|
self.assertFalse(response)
|
|
@ -0,0 +1,9 @@
|
||||||
|
from django.conf.urls.defaults import *
|
||||||
|
import views
|
||||||
|
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
(r'^get_view/$', views.get_view),
|
||||||
|
(r'^post_view/$', views.post_view),
|
||||||
|
(r'^redirect_view/$', views.redirect_view),
|
||||||
|
(r'^login_protected_view/$', views.login_protected_view),
|
||||||
|
)
|
|
@ -0,0 +1,35 @@
|
||||||
|
from django.template import Context, Template
|
||||||
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
|
||||||
|
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')
|
||||||
|
c = Context({'var': 42})
|
||||||
|
|
||||||
|
return HttpResponse(t.render(c))
|
||||||
|
|
||||||
|
def post_view(request):
|
||||||
|
"""A view that expects a POST, and returns a different template depending
|
||||||
|
on whether any POST data is available
|
||||||
|
"""
|
||||||
|
if request.POST:
|
||||||
|
t = Template('Data received: {{ data }} is the value.', name='POST Template')
|
||||||
|
c = Context({'data': request.POST['value']})
|
||||||
|
else:
|
||||||
|
t = Template('Viewing POST page.', name='Empty POST Template')
|
||||||
|
c = Context()
|
||||||
|
|
||||||
|
return HttpResponse(t.render(c))
|
||||||
|
|
||||||
|
def redirect_view(request):
|
||||||
|
"A view that redirects all requests to the GET view"
|
||||||
|
return HttpResponseRedirect('/test_client/get_view/')
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def login_protected_view(request):
|
||||||
|
"A simple view that is login protected."
|
||||||
|
t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template')
|
||||||
|
c = Context({'user': request.user})
|
||||||
|
|
||||||
|
return HttpResponse(t.render(c))
|
|
@ -6,6 +6,7 @@ import unittest
|
||||||
MODEL_TESTS_DIR_NAME = 'modeltests'
|
MODEL_TESTS_DIR_NAME = 'modeltests'
|
||||||
REGRESSION_TESTS_DIR_NAME = 'regressiontests'
|
REGRESSION_TESTS_DIR_NAME = 'regressiontests'
|
||||||
TEST_DATABASE_NAME = 'django_test_db'
|
TEST_DATABASE_NAME = 'django_test_db'
|
||||||
|
TEST_TEMPLATE_DIR = 'templates'
|
||||||
|
|
||||||
MODEL_TEST_DIR = os.path.join(os.path.dirname(__file__), MODEL_TESTS_DIR_NAME)
|
MODEL_TEST_DIR = os.path.join(os.path.dirname(__file__), MODEL_TESTS_DIR_NAME)
|
||||||
REGRESSION_TEST_DIR = os.path.join(os.path.dirname(__file__), REGRESSION_TESTS_DIR_NAME)
|
REGRESSION_TEST_DIR = os.path.join(os.path.dirname(__file__), REGRESSION_TESTS_DIR_NAME)
|
||||||
|
@ -71,17 +72,23 @@ class InvalidModelTestCase(unittest.TestCase):
|
||||||
def django_tests(verbosity, tests_to_run):
|
def django_tests(verbosity, tests_to_run):
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models.loading import get_apps, load_app
|
from django.db.models.loading import get_apps, load_app
|
||||||
|
|
||||||
old_installed_apps = settings.INSTALLED_APPS
|
old_installed_apps = settings.INSTALLED_APPS
|
||||||
old_test_database_name = settings.TEST_DATABASE_NAME
|
old_test_database_name = settings.TEST_DATABASE_NAME
|
||||||
|
old_root_urlconf = settings.ROOT_URLCONF
|
||||||
|
old_template_dirs = settings.TEMPLATE_DIRS
|
||||||
|
|
||||||
|
# Redirect some settings for the duration of these tests
|
||||||
settings.TEST_DATABASE_NAME = TEST_DATABASE_NAME
|
settings.TEST_DATABASE_NAME = TEST_DATABASE_NAME
|
||||||
settings.INSTALLED_APPS = ALWAYS_INSTALLED_APPS
|
settings.INSTALLED_APPS = ALWAYS_INSTALLED_APPS
|
||||||
|
settings.ROOT_URLCONF = 'urls'
|
||||||
|
settings.TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), TEST_TEMPLATE_DIR),)
|
||||||
|
|
||||||
# load all the ALWAYS_INSTALLED_APPS
|
# load all the ALWAYS_INSTALLED_APPS
|
||||||
get_apps()
|
get_apps()
|
||||||
|
|
||||||
test_models = []
|
|
||||||
# Load all the test model apps
|
# Load all the test model apps
|
||||||
|
test_models = []
|
||||||
for model_dir, model_name in get_test_models():
|
for model_dir, model_name in get_test_models():
|
||||||
model_label = '.'.join([model_dir, model_name])
|
model_label = '.'.join([model_dir, model_name])
|
||||||
try:
|
try:
|
||||||
|
@ -112,6 +119,9 @@ def django_tests(verbosity, tests_to_run):
|
||||||
# Restore the old settings
|
# Restore the old settings
|
||||||
settings.INSTALLED_APPS = old_installed_apps
|
settings.INSTALLED_APPS = old_installed_apps
|
||||||
settings.TESTS_DATABASE_NAME = old_test_database_name
|
settings.TESTS_DATABASE_NAME = old_test_database_name
|
||||||
|
settings.ROOT_URLCONF = old_root_urlconf
|
||||||
|
settings.TEMPLATE_DIRS = old_template_dirs
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
usage = "%prog [options] [model model model ...]"
|
usage = "%prog [options] [model model model ...]"
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Django Internal Tests: 404 Error
|
|
@ -0,0 +1 @@
|
||||||
|
Django Internal Tests: 500 Error
|
|
@ -0,0 +1,19 @@
|
||||||
|
<html>
|
||||||
|
<head></head>
|
||||||
|
<body>
|
||||||
|
<h1>Django Internal Tests: Login</h1>
|
||||||
|
{% if form.has_errors %}
|
||||||
|
<p>Your username and password didn't match. Please try again.</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="post" action=".">
|
||||||
|
<table>
|
||||||
|
<tr><td><label for="id_username">Username:</label></td><td>{{ form.username }}</td></tr>
|
||||||
|
<tr><td><label for="id_password">Password:</label></td><td>{{ form.password }}</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<input type="submit" value="login" />
|
||||||
|
<input type="hidden" name="next" value="{{ next }}" />
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,10 @@
|
||||||
|
from django.conf.urls.defaults import *
|
||||||
|
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
# test_client modeltest urls
|
||||||
|
(r'^test_client/', include('modeltests.test_client.urls')),
|
||||||
|
|
||||||
|
# Always provide the auth system login and logout views
|
||||||
|
(r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}),
|
||||||
|
(r'^accounts/logout/$', 'django.contrib.auth.views.login'),
|
||||||
|
)
|
Loading…
Reference in New Issue