Ticket #4788: testing-patches.diff

File testing-patches.diff, 14.2 KB (added by Samuel Cormier-Iijima, 16 years ago)

Updated patch for Django >= 1.0

  • django/test/simple.py

     
    1 import unittest
     1import sys, time, traceback, unittest
    22from django.conf import settings
    33from django.db.models import get_app, get_apps
    44from django.test import _doctest as doctest
     
    140140    old_name = settings.DATABASE_NAME
    141141    from django.db import connection
    142142    connection.creation.create_test_db(verbosity, autoclobber=not interactive)
    143     result = unittest.TextTestRunner(verbosity=verbosity).run(suite)
     143    result = SkipTestRunner(verbosity=verbosity).run(suite)
    144144    connection.creation.destroy_test_db(old_name, verbosity)
    145145   
    146146    teardown_test_environment()
    147147   
    148148    return len(result.failures) + len(result.errors)
     149
     150class SkipTestRunner:
     151    """
     152    A test runner class that adds a Skipped category in the output layer.
     153   
     154    Modeled after unittest.TextTestRunner.
     155
     156    Similarly to unittest.TextTestRunner, prints summary of the results at the end.
     157    (Including a count of skipped tests.)
     158    """
     159
     160    def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1):
     161        self.stream = unittest._WritelnDecorator(stream)
     162        self.descriptions = descriptions
     163        self.verbosity = verbosity
     164        self.result = _SkipTestResult(self.stream, descriptions, verbosity)
     165
     166    def run(self, test):
     167        "Run the given test case or test suite."
     168        startTime = time.time()
     169        test.run(self.result)
     170        stopTime = time.time()
     171        timeTaken = stopTime - startTime
     172       
     173        self.result.printErrors()
     174        self.stream.writeln(self.result.separator2)
     175        run = self.result.testsRun
     176        self.stream.writeln('Ran %d test%s in %.3fs' %
     177                (run, run != 1 and 's' or '', timeTaken))
     178        self.stream.writeln()
     179        if not self.result.wasSuccessful():
     180            self.stream.write('FAILED (')
     181            failed, errored, skipped = map(len, (self.result.failures, self.result.errors, self.result.skipped))
     182            if failed:
     183                self.stream.write('failures=%d' % failed)
     184            if errored:
     185                if failed: self.stream.write(', ')
     186                self.stream.write('errors=%d' % errored)
     187            if skipped:
     188                if errored or failed: self.stream.write(', ')
     189                self.stream.write('skipped=%d' % skipped)
     190            self.stream.writeln(')')
     191        else:
     192            self.stream.writeln('OK')
     193        return self.result
     194
     195class _SkipTestResult(unittest._TextTestResult):
     196    """
     197    A test result class that adds a Skipped category in the output layer.
     198   
     199    Modeled after unittest._TextTestResult.
     200   
     201    Similarly to unittest._TextTestResult, prints out the names of tests as they are
     202    run and errors as they occur.
     203    """
     204
     205    def __init__(self, stream, descriptions, verbosity):
     206        unittest._TextTestResult.__init__(self, stream, descriptions, verbosity)
     207        self.skipped = []
     208
     209    def addError(self, test, err):
     210        # Determine if this is a skipped test
     211        tracebacks = traceback.extract_tb(err[2])
     212        if tracebacks[-1][-1].startswith('raise SkippedTest'):
     213            self.skipped.append((test, self._exc_info_to_string(err, test)))
     214            if self.showAll:
     215                self.stream.writeln('SKIPPED')
     216            elif self.dots:
     217                self.stream.write('S')
     218                self.stream.flush()
     219        else:
     220            unittest.TestResult.addError(self, test, err)
     221            if self.showAll:
     222                self.stream.writeln('ERROR')
     223            elif self.dots:
     224                self.stream.write('E')
     225                self.stream.flush()
     226
     227    def printErrors(self):
     228        if self.dots or self.showAll:
     229            self.stream.writeln()
     230        self.printErrorList('SKIPPED', self.skipped)
     231        self.printErrorList('ERROR', self.errors)
     232        self.printErrorList('FAIL', self.failures)
  • django/test/__init__.py

     
    44
    55from django.test.client import Client
    66from django.test.testcases import TestCase
     7
     8class SkippedTest(Exception):
     9    def __init__(self, reason):
     10        self.reason = reason
     11       
     12    def __str__(self):
     13        return self.reason
  • django/test/decorators.py

     
     1from django.core import urlresolvers
     2from django.test import SkippedTest
     3
     4def views_required(required_views=[]):
     5    def urls_found():
     6        try:
     7            for view in required_views:
     8                urlresolvers.reverse(view)
     9            return True
     10        except urlresolvers.NoReverseMatch:
     11            return False
     12    reason = 'Required view%s for this test not found: %s' % \
     13            (len(required_views) > 1 and 's' or '', ', '.join(required_views))
     14    return conditional_skip(urls_found, reason=reason)
     15
     16def modules_required(required_modules=[]):
     17    def modules_found():
     18        try:
     19            for module in required_modules:
     20                __import__(module)
     21            return True
     22        except ImportError:
     23            return False
     24    reason = 'Required module%s for this test not found: %s' % \
     25            (len(required_modules) > 1 and 's' or '', ', '.join(required_modules))
     26    return conditional_skip(modules_found, reason=reason)
     27
     28def skip_specific_database(database_engine):
     29    def database_check():
     30        from django.conf import settings
     31        return database_engine == settings.DATABASE_ENGINE
     32    reason = 'Test not run for database engine %s.' % database_engine
     33    return conditional_skip(database_check, reason=reason)
     34
     35def conditional_skip(required_condition, reason=''):
     36    if required_condition():
     37        return lambda x: x
     38    else:
     39        return skip_test(reason)
     40
     41def skip_test(reason=''):
     42    def _skip(x):
     43        raise SkippedTest(reason=reason)
     44    return lambda x: _skip
  • django/contrib/auth/tests/views.py

     
    55from django.conf import settings
    66from django.contrib.auth.models import User
    77from django.test import TestCase
     8from django.test.decorators import views_required
    89from django.core import mail
     10from django.core.urlresolvers import reverse
    911
    1012class PasswordResetTest(TestCase):
    1113    fixtures = ['authtestdata.json']
    12     urls = 'django.contrib.auth.urls'
    1314
    1415    def test_email_not_found(self):
    1516        "Error is raised if the provided email address isn't currently registered"
    16         response = self.client.get('/password_reset/')
     17        response = self.client.get(reverse('django.contrib.auth.views.password_reset'))
    1718        self.assertEquals(response.status_code, 200)
    18         response = self.client.post('/password_reset/', {'email': 'not_a_real_email@email.com'})
     19        response = self.client.post(reverse('django.contrib.auth.views.password_reset'), {'email': 'not_a_real_email@email.com'})
    1920        self.assertContains(response, "That e-mail address doesn't have an associated user account")
    2021        self.assertEquals(len(mail.outbox), 0)
     22    test_email_not_found = views_required(required_views=['django.contrib.auth.views.password_reset'])(test_email_not_found)
    2123
    2224    def test_email_found(self):
    2325        "Email is sent if a valid email address is provided for password reset"
    24         response = self.client.post('/password_reset/', {'email': 'staffmember@example.com'})
     26        response = self.client.post(reverse('django.contrib.auth.views.password_reset'), {'email': 'staffmember@example.com'})
    2527        self.assertEquals(response.status_code, 302)
    2628        self.assertEquals(len(mail.outbox), 1)
    2729        self.assert_("http://" in mail.outbox[0].body)
     30    test_email_found = views_required(required_views=['django.contrib.auth.views.password_reset'])(test_email_found)
    2831
    2932    def _test_confirm_start(self):
    3033        # Start by creating the email
    31         response = self.client.post('/password_reset/', {'email': 'staffmember@example.com'})
     34        response = self.client.post(reverse('django.contrib.auth.views.password_reset'), {'email': 'staffmember@example.com'})
    3235        self.assertEquals(response.status_code, 302)
    3336        self.assertEquals(len(mail.outbox), 1)
    3437        return self._read_signup_email(mail.outbox[0])
     
    4447        # redirect to a 'complete' page:
    4548        self.assertEquals(response.status_code, 200)
    4649        self.assert_("Please enter your new password" in response.content)
     50    test_confirm_valid = views_required(required_views=['django.contrib.auth.views.password_reset'])(test_confirm_valid)
    4751
     52
    4853    def test_confirm_invalid(self):
    4954        url, path = self._test_confirm_start()
    5055        # Lets munge the token in the path, but keep the same length,
     
    5459        response = self.client.get(path)
    5560        self.assertEquals(response.status_code, 200)
    5661        self.assert_("The password reset link was invalid" in response.content)
     62    test_confirm_invalid = views_required(required_views=['django.contrib.auth.views.password_reset'])(test_confirm_invalid)
    5763
    5864    def test_confirm_invalid_post(self):
    5965        # Same as test_confirm_invalid, but trying
     
    6672        # Check the password has not been changed
    6773        u = User.objects.get(email='staffmember@example.com')
    6874        self.assert_(not u.check_password("anewpassword"))
     75    test_confirm_invalid_post = views_required(required_views=['django.contrib.auth.views.password_reset'])(test_confirm_invalid_post)
    6976
    7077    def test_confirm_complete(self):
    7178        url, path = self._test_confirm_start()
     
    8188        response = self.client.get(path)
    8289        self.assertEquals(response.status_code, 200)
    8390        self.assert_("The password reset link was invalid" in response.content)
     91    test_confirm_complete = views_required(required_views=['django.contrib.auth.views.password_reset'])(test_confirm_complete)
    8492
    8593    def test_confirm_different_passwords(self):
    8694        url, path = self._test_confirm_start()
     
    8896                                           'new_password2':' x'})
    8997        self.assertEquals(response.status_code, 200)
    9098        self.assert_("The two password fields didn't match" in response.content)
     99    test_confirm_different_passwords = views_required(required_views=['django.contrib.auth.views.password_reset'])(test_confirm_different_passwords)
    91100
    92 
    93101class ChangePasswordTest(TestCase):
    94102    fixtures = ['authtestdata.json']
    95103    urls = 'django.contrib.auth.urls'
  • tests/regressiontests/test_decorators/tests.py

     
     1"""
     2>>> from django.test import SkippedTest
     3>>> from django.test.decorators import *
     4
     5>>> skip_test()(None)(None)
     6Traceback (most recent call last):
     7    ...
     8SkippedTest
     9
     10>>> skip_test(reason='testing')(None)(None)
     11Traceback (most recent call last):
     12    ...
     13SkippedTest: testing
     14
     15>>> conditional_skip(lambda: False)(None)(None)
     16Traceback (most recent call last):
     17    ...
     18SkippedTest
     19
     20>>> conditional_skip(lambda: True)(lambda: True)()
     21True
     22
     23"""
  • docs/topics/testing.txt

     
    865865This test case will use the contents of ``myapp.test_urls`` as the
    866866URLconf for the duration of the test case.
    867867
     868Skipping tests bound to fail
     869~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     870
     871**New in Django development version**
     872
     873Occasionally it's helpful to specify tests that are skipped under certain
     874circumstances. To accomplish this, the Django test framework offers decorators
     875that you can apply to your test methods for them to be conditionally skipped.
     876
     877You can supply your own condition function as follows::
     878
     879    from django.tests.decorators import *
     880   
     881    class TestUnderCondition(TestCase):
     882   
     883        def _my_condition():
     884            # Condition returning True if test should be run and False if it
     885            # should be skipped.
     886
     887        @conditional_skip(_my_condition, reason='This test should be skipped sometimes')
     888        def testOnlyOnTuesday(self):
     889            # Test to run if _my_condition evaluates to True
     890
     891In addition, the Django test framework supplies a handful of skip conditions that
     892handle commonly used conditions for skipping tests.
     893
     894``views_required(required_views=[])``
     895    Does a ``urlresolver.Reverse`` on the required views supplied. Runs test only if
     896    all views in ``required_views`` are in use.
     897
     898``modules_required(required_modules=[])``
     899    Runs tests only if all modules in ``required_modules`` can be imported.
     900
     901``skip_specific_database(database_engine)``
     902    Skips test if ``settings.DATABASE_ENGINE`` is equal to database_engine.
     903
     904If a test is skipped, it is added to a skipped category in the test runner and
     905the test results are reported as such::
     906
     907    ======================================================================
     908    SKIPPED: test_email_found (django.contrib.auth.tests.basic.PasswordResetTest)
     909    ----------------------------------------------------------------------
     910    Traceback (most recent call last):
     911      File "/Users/dnaquin/Dropbox/Sandbox/django/django/test/decorators.py", line 43, in _skip
     912        raise SkippedTest(reason=reason)
     913    SkippedTest: Required view for this test not found: django.contrib.auth.views.password_reset
     914
     915    ----------------------------------------------------------------------
     916    Ran 408 tests in 339.663s
     917
     918    FAILED (failures=1, skipped=2)
     919
    868920.. _emptying-test-outbox:
    869921
    870922Emptying the test outbox
Back to Top