mirror of
https://github.com/django/django.git
synced 2025-07-05 18:29:11 +00:00
[gsoc2009-testing] Added support for skipping tests that cannot pass. Add auth to regression suite urls.py so reverse() works.
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/test-improvements@11142 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
8cd4df145f
commit
f2be59849c
@ -9,6 +9,7 @@ from django.contrib.auth.models import User
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.test.decorators import views_required
|
||||||
|
|
||||||
class AuthViewsTestCase(TestCase):
|
class AuthViewsTestCase(TestCase):
|
||||||
"""
|
"""
|
||||||
@ -49,22 +50,26 @@ class PasswordResetTest(AuthViewsTestCase):
|
|||||||
|
|
||||||
def test_email_not_found(self):
|
def test_email_not_found(self):
|
||||||
"Error is raised if the provided email address isn't currently registered"
|
"Error is raised if the provided email address isn't currently registered"
|
||||||
response = self.client.get('/password_reset/')
|
response = self.client.get(reverse('django.contrib.auth.views.password_reset'))
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEquals(response.status_code, 200)
|
||||||
response = self.client.post('/password_reset/', {'email': 'not_a_real_email@email.com'})
|
response = self.client.post(reverse('django.contrib.auth.views.password_reset'), {'email': 'not_a_real_email@email.com'})
|
||||||
self.assertContains(response, "That e-mail address doesn't have an associated user account")
|
self.assertContains(response, "That e-mail address doesn't have an associated user account")
|
||||||
self.assertEquals(len(mail.outbox), 0)
|
self.assertEquals(len(mail.outbox), 0)
|
||||||
|
|
||||||
|
test_email_not_found = views_required(required_views=['django.contrib.auth.views.password_reset'])(test_email_not_found)
|
||||||
|
|
||||||
def test_email_found(self):
|
def test_email_found(self):
|
||||||
"Email is sent if a valid email address is provided for password reset"
|
"Email is sent if a valid email address is provided for password reset"
|
||||||
response = self.client.post('/password_reset/', {'email': 'staffmember@example.com'})
|
response = self.client.post(reverse('django.contrib.auth.views.password_reset'), {'email': 'staffmember@example.com'})
|
||||||
self.assertEquals(response.status_code, 302)
|
self.assertEquals(response.status_code, 302)
|
||||||
self.assertEquals(len(mail.outbox), 1)
|
self.assertEquals(len(mail.outbox), 1)
|
||||||
self.assert_("http://" in mail.outbox[0].body)
|
self.assert_("http://" in mail.outbox[0].body)
|
||||||
|
|
||||||
|
test_email_found = views_required(required_views=['django.contrib.auth.views.password_reset'])(test_email_found)
|
||||||
|
|
||||||
def _test_confirm_start(self):
|
def _test_confirm_start(self):
|
||||||
# Start by creating the email
|
# Start by creating the email
|
||||||
response = self.client.post('/password_reset/', {'email': 'staffmember@example.com'})
|
response = self.client.post(reverse('django.contrib.auth.views.password_reset'), {'email': 'staffmember@example.com'})
|
||||||
self.assertEquals(response.status_code, 302)
|
self.assertEquals(response.status_code, 302)
|
||||||
self.assertEquals(len(mail.outbox), 1)
|
self.assertEquals(len(mail.outbox), 1)
|
||||||
return self._read_signup_email(mail.outbox[0])
|
return self._read_signup_email(mail.outbox[0])
|
||||||
@ -80,6 +85,8 @@ class PasswordResetTest(AuthViewsTestCase):
|
|||||||
# redirect to a 'complete' page:
|
# redirect to a 'complete' page:
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEquals(response.status_code, 200)
|
||||||
self.assert_("Please enter your new password" in response.content)
|
self.assert_("Please enter your new password" in response.content)
|
||||||
|
test_confirm_valid = views_required(required_views=['django.contrib.auth.views.password_reset'])(test_confirm_valid)
|
||||||
|
|
||||||
|
|
||||||
def test_confirm_invalid(self):
|
def test_confirm_invalid(self):
|
||||||
url, path = self._test_confirm_start()
|
url, path = self._test_confirm_start()
|
||||||
@ -90,6 +97,7 @@ class PasswordResetTest(AuthViewsTestCase):
|
|||||||
response = self.client.get(path)
|
response = self.client.get(path)
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEquals(response.status_code, 200)
|
||||||
self.assert_("The password reset link was invalid" in response.content)
|
self.assert_("The password reset link was invalid" in response.content)
|
||||||
|
test_confirm_invalid = views_required(required_views=['django.contrib.auth.views.password_reset'])(test_confirm_invalid)
|
||||||
|
|
||||||
def test_confirm_invalid_post(self):
|
def test_confirm_invalid_post(self):
|
||||||
# Same as test_confirm_invalid, but trying
|
# Same as test_confirm_invalid, but trying
|
||||||
@ -102,6 +110,7 @@ class PasswordResetTest(AuthViewsTestCase):
|
|||||||
# Check the password has not been changed
|
# Check the password has not been changed
|
||||||
u = User.objects.get(email='staffmember@example.com')
|
u = User.objects.get(email='staffmember@example.com')
|
||||||
self.assert_(not u.check_password("anewpassword"))
|
self.assert_(not u.check_password("anewpassword"))
|
||||||
|
test_confirm_invalid_post = views_required(required_views=['django.contrib.auth.views.password_reset'])(test_confirm_invalid_post)
|
||||||
|
|
||||||
def test_confirm_complete(self):
|
def test_confirm_complete(self):
|
||||||
url, path = self._test_confirm_start()
|
url, path = self._test_confirm_start()
|
||||||
@ -117,6 +126,7 @@ class PasswordResetTest(AuthViewsTestCase):
|
|||||||
response = self.client.get(path)
|
response = self.client.get(path)
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEquals(response.status_code, 200)
|
||||||
self.assert_("The password reset link was invalid" in response.content)
|
self.assert_("The password reset link was invalid" in response.content)
|
||||||
|
test_confirm_complete = views_required(required_views=['django.contrib.auth.views.password_reset'])(test_confirm_complete)
|
||||||
|
|
||||||
def test_confirm_different_passwords(self):
|
def test_confirm_different_passwords(self):
|
||||||
url, path = self._test_confirm_start()
|
url, path = self._test_confirm_start()
|
||||||
@ -124,7 +134,8 @@ class PasswordResetTest(AuthViewsTestCase):
|
|||||||
'new_password2':' x'})
|
'new_password2':' x'})
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEquals(response.status_code, 200)
|
||||||
self.assert_("The two password fields didn't match" in response.content)
|
self.assert_("The two password fields didn't match" in response.content)
|
||||||
|
|
||||||
|
test_confirm_different_passwords = views_required(required_views=['django.contrib.auth.views.password_reset'])(test_confirm_different_passwords)
|
||||||
class ChangePasswordTest(AuthViewsTestCase):
|
class ChangePasswordTest(AuthViewsTestCase):
|
||||||
|
|
||||||
def login(self, password='password'):
|
def login(self, password='password'):
|
||||||
|
@ -4,3 +4,10 @@ Django Unit Test and Doctest framework.
|
|||||||
|
|
||||||
from django.test.client import Client
|
from django.test.client import Client
|
||||||
from django.test.testcases import TestCase, TransactionTestCase
|
from django.test.testcases import TestCase, TransactionTestCase
|
||||||
|
|
||||||
|
class SkippedTest(Exception):
|
||||||
|
def __init__(self, reason):
|
||||||
|
self.reason = reason
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.reason
|
44
django/test/decorators.py
Normal file
44
django/test/decorators.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
from django.core import urlresolvers
|
||||||
|
from django.test import SkippedTest
|
||||||
|
|
||||||
|
def views_required(required_views=[]):
|
||||||
|
def urls_found():
|
||||||
|
try:
|
||||||
|
for view in required_views:
|
||||||
|
urlresolvers.reverse(view)
|
||||||
|
return True
|
||||||
|
except urlresolvers.NoReverseMatch:
|
||||||
|
return False
|
||||||
|
reason = 'Required view%s for this test not found: %s' % \
|
||||||
|
(len(required_views) > 1 and 's' or '', ', '.join(required_views))
|
||||||
|
return conditional_skip(urls_found, reason=reason)
|
||||||
|
|
||||||
|
def modules_required(required_modules=[]):
|
||||||
|
def modules_found():
|
||||||
|
try:
|
||||||
|
for module in required_modules:
|
||||||
|
__import__(module)
|
||||||
|
return True
|
||||||
|
except ImportError:
|
||||||
|
return False
|
||||||
|
reason = 'Required module%s for this test not found: %s' % \
|
||||||
|
(len(required_modules) > 1 and 's' or '', ', '.join(required_modules))
|
||||||
|
return conditional_skip(modules_found, reason=reason)
|
||||||
|
|
||||||
|
def skip_specific_database(database_engine):
|
||||||
|
def database_check():
|
||||||
|
from django.conf import settings
|
||||||
|
return database_engine == settings.DATABASE_ENGINE
|
||||||
|
reason = 'Test not run for database engine %s.' % database_engine
|
||||||
|
return conditional_skip(database_check, reason=reason)
|
||||||
|
|
||||||
|
def conditional_skip(required_condition, reason=''):
|
||||||
|
if required_condition():
|
||||||
|
return lambda x: x
|
||||||
|
else:
|
||||||
|
return skip_test(reason)
|
||||||
|
|
||||||
|
def skip_test(reason=''):
|
||||||
|
def _skip(x):
|
||||||
|
raise SkippedTest(reason=reason)
|
||||||
|
return lambda x: _skip
|
@ -1,4 +1,4 @@
|
|||||||
import unittest
|
import sys, time, traceback, unittest
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models import get_app, get_apps
|
from django.db.models import get_app, get_apps
|
||||||
from django.test import _doctest as doctest
|
from django.test import _doctest as doctest
|
||||||
@ -202,9 +202,95 @@ class DefaultTestRunner(object):
|
|||||||
old_name = settings.DATABASE_NAME
|
old_name = settings.DATABASE_NAME
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
connection.creation.create_test_db(verbosity, autoclobber=not interactive)
|
connection.creation.create_test_db(verbosity, autoclobber=not interactive)
|
||||||
result = unittest.TextTestRunner(verbosity=verbosity).run(suite)
|
result = SkipTestRunner(verbosity=verbosity).run(suite)
|
||||||
connection.creation.destroy_test_db(old_name, verbosity)
|
connection.creation.destroy_test_db(old_name, verbosity)
|
||||||
|
|
||||||
teardown_test_environment()
|
teardown_test_environment()
|
||||||
|
|
||||||
return len(result.failures) + len(result.errors)
|
return len(result.failures) + len(result.errors)
|
||||||
|
|
||||||
|
|
||||||
|
class SkipTestRunner:
|
||||||
|
"""
|
||||||
|
A test runner class that adds a Skipped category in the output layer.
|
||||||
|
|
||||||
|
Modeled after unittest.TextTestRunner.
|
||||||
|
|
||||||
|
Similarly to unittest.TextTestRunner, prints summary of the results at the end.
|
||||||
|
(Including a count of skipped tests.)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1):
|
||||||
|
self.stream = unittest._WritelnDecorator(stream)
|
||||||
|
self.descriptions = descriptions
|
||||||
|
self.verbosity = verbosity
|
||||||
|
self.result = _SkipTestResult(self.stream, descriptions, verbosity)
|
||||||
|
|
||||||
|
def run(self, test):
|
||||||
|
"Run the given test case or test suite."
|
||||||
|
startTime = time.time()
|
||||||
|
test.run(self.result)
|
||||||
|
stopTime = time.time()
|
||||||
|
timeTaken = stopTime - startTime
|
||||||
|
|
||||||
|
self.result.printErrors()
|
||||||
|
self.stream.writeln(self.result.separator2)
|
||||||
|
run = self.result.testsRun
|
||||||
|
self.stream.writeln('Ran %d test%s in %.3fs' %
|
||||||
|
(run, run != 1 and 's' or '', timeTaken))
|
||||||
|
self.stream.writeln()
|
||||||
|
if not self.result.wasSuccessful():
|
||||||
|
self.stream.write('FAILED (')
|
||||||
|
failed, errored, skipped = map(len, (self.result.failures, self.result.errors, self.result.skipped))
|
||||||
|
if failed:
|
||||||
|
self.stream.write('failures=%d' % failed)
|
||||||
|
if errored:
|
||||||
|
if failed: self.stream.write(', ')
|
||||||
|
self.stream.write('errors=%d' % errored)
|
||||||
|
if skipped:
|
||||||
|
if errored or failed: self.stream.write(', ')
|
||||||
|
self.stream.write('skipped=%d' % skipped)
|
||||||
|
self.stream.writeln(')')
|
||||||
|
else:
|
||||||
|
self.stream.writeln('OK')
|
||||||
|
return self.result
|
||||||
|
|
||||||
|
class _SkipTestResult(unittest._TextTestResult):
|
||||||
|
"""
|
||||||
|
A test result class that adds a Skipped category in the output layer.
|
||||||
|
|
||||||
|
Modeled after unittest._TextTestResult.
|
||||||
|
|
||||||
|
Similarly to unittest._TextTestResult, prints out the names of tests as they are
|
||||||
|
run and errors as they occur.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, stream, descriptions, verbosity):
|
||||||
|
unittest._TextTestResult.__init__(self, stream, descriptions, verbosity)
|
||||||
|
self.skipped = []
|
||||||
|
|
||||||
|
def addError(self, test, err):
|
||||||
|
# Determine if this is a skipped test
|
||||||
|
tracebacks = traceback.extract_tb(err[2])
|
||||||
|
if tracebacks[-1][-1].startswith('raise SkippedTest'):
|
||||||
|
self.skipped.append((test, self._exc_info_to_string(err, test)))
|
||||||
|
if self.showAll:
|
||||||
|
self.stream.writeln('SKIPPED')
|
||||||
|
elif self.dots:
|
||||||
|
self.stream.write('S')
|
||||||
|
self.stream.flush()
|
||||||
|
else:
|
||||||
|
unittest.TestResult.addError(self, test, err)
|
||||||
|
if self.showAll:
|
||||||
|
self.stream.writeln('ERROR')
|
||||||
|
elif self.dots:
|
||||||
|
self.stream.write('E')
|
||||||
|
self.stream.flush()
|
||||||
|
|
||||||
|
def printErrors(self):
|
||||||
|
if self.dots or self.showAll:
|
||||||
|
self.stream.writeln()
|
||||||
|
self.printErrorList('SKIPPED', self.skipped)
|
||||||
|
self.printErrorList('ERROR', self.errors)
|
||||||
|
self.printErrorList('FAIL', self.failures)
|
||||||
|
|
||||||
|
@ -1045,6 +1045,58 @@ For example::
|
|||||||
This test case will load the contents of ``myapp.test_models`` and add
|
This test case will load the contents of ``myapp.test_models`` and add
|
||||||
any subclass of ``django.db.models.Model`` to ``myapp.models``.
|
any subclass of ``django.db.models.Model`` to ``myapp.models``.
|
||||||
|
|
||||||
|
Skipping tests bound to fail
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. versionadded: 1.1
|
||||||
|
|
||||||
|
Occasionally it's helpful to specify tests that are skipped under certain
|
||||||
|
circumstances. To accomplish this, the Django test framework offers decorators
|
||||||
|
that you can apply to your test methods for them to be conditionally skipped.
|
||||||
|
|
||||||
|
You can supply your own condition function as follows::
|
||||||
|
|
||||||
|
from django.tests.decorators import *
|
||||||
|
|
||||||
|
class TestUnderCondition(TestCase):
|
||||||
|
|
||||||
|
def _my_condition():
|
||||||
|
# Condition returning True if test should be run and False if it
|
||||||
|
# should be skipped.
|
||||||
|
|
||||||
|
@conditional_skip(_my_condition, reason='This test should be skipped sometimes')
|
||||||
|
def testOnlyOnTuesday(self):
|
||||||
|
# Test to run if _my_condition evaluates to True
|
||||||
|
|
||||||
|
In addition, the Django test framework supplies a handful of skip conditions that
|
||||||
|
handle commonly used conditions for skipping tests.
|
||||||
|
|
||||||
|
``views_required(required_views=[])``
|
||||||
|
Does a ``urlresolver.Reverse`` on the required views supplied. Runs test only if
|
||||||
|
all views in ``required_views`` are in use.
|
||||||
|
|
||||||
|
``modules_required(required_modules=[])``
|
||||||
|
Runs tests only if all modules in ``required_modules`` can be imported.
|
||||||
|
|
||||||
|
``skip_specific_database(database_engine)``
|
||||||
|
Skips test if ``settings.DATABASE_ENGINE`` is equal to database_engine.
|
||||||
|
|
||||||
|
If a test is skipped, it is added to a skipped category in the test runner and
|
||||||
|
the test results are reported as such::
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
SKIPPED: test_email_found (django.contrib.auth.tests.basic.PasswordResetTest)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/Users/dnaquin/Dropbox/Sandbox/django/django/test/decorators.py", line 43, in _skip
|
||||||
|
raise SkippedTest(reason=reason)
|
||||||
|
SkippedTest: Required view for this test not found: django.contrib.auth.views.password_reset
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Ran 408 tests in 339.663s
|
||||||
|
|
||||||
|
FAILED (failures=1, skipped=2)
|
||||||
|
|
||||||
.. _emptying-test-outbox:
|
.. _emptying-test-outbox:
|
||||||
|
|
||||||
Emptying the test outbox
|
Emptying the test outbox
|
||||||
|
@ -2,7 +2,7 @@ from django.conf.urls.defaults import *
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
import views
|
import views
|
||||||
import customadmin
|
import customadmin
|
||||||
admin.autodiscover()
|
#admin.autodiscover()
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||||
(r'^admin/secure-view/$', views.secure_view),
|
(r'^admin/secure-view/$', views.secure_view),
|
||||||
|
23
tests/regressiontests/test_decorators/tests.py
Normal file
23
tests/regressiontests/test_decorators/tests.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
"""
|
||||||
|
>>> from django.test import SkippedTest
|
||||||
|
>>> from django.test.decorators import *
|
||||||
|
|
||||||
|
>>> skip_test()(None)(None)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
SkippedTest
|
||||||
|
|
||||||
|
>>> skip_test(reason='testing')(None)(None)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
SkippedTest: testing
|
||||||
|
|
||||||
|
>>> conditional_skip(lambda: False)(None)(None)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
SkippedTest
|
||||||
|
|
||||||
|
>>> conditional_skip(lambda: True)(lambda: True)()
|
||||||
|
True
|
||||||
|
|
||||||
|
"""
|
@ -11,6 +11,7 @@ urlpatterns = patterns('',
|
|||||||
# Always provide the auth system login and logout views
|
# Always provide the auth system login and logout views
|
||||||
(r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}),
|
(r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}),
|
||||||
(r'^accounts/logout/$', 'django.contrib.auth.views.logout'),
|
(r'^accounts/logout/$', 'django.contrib.auth.views.logout'),
|
||||||
|
(r'^accounts2/', include('django.contrib.auth.urls')),
|
||||||
|
|
||||||
# test urlconf for {% url %} template tag
|
# test urlconf for {% url %} template tag
|
||||||
(r'^url_tag/', include('regressiontests.templates.urls')),
|
(r'^url_tag/', include('regressiontests.templates.urls')),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user