Django

Code

Changeset 5157

Show
Ignore:
Timestamp:
05/07/07 10:50:55 (1 year ago)
Author:
bouldersprinters
Message:

boulder-oracle-sprint: Merged to [5156]

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/boulder-oracle-sprint/django/db/models/query.py

    r5135 r5157  
    999999            field_choices(current_opts.get_all_related_objects(), True) + \ 
    10001000            field_choices(current_opts.fields, False) 
    1001         raise TypeError, "Cannot resolve keyword '%s' into field, choices are: %s" % (name, ", ".join(choices)) 
     1001        raise TypeError, "Cannot resolve keyword '%s' into field. Choices are: %s" % (name, ", ".join(choices)) 
    10021002 
    10031003    # Check whether an intermediate join is required between current_table 
  • django/branches/boulder-oracle-sprint/django/test/client.py

    r4777 r5157  
     1import datetime 
    12import sys 
    23from cStringIO import StringIO 
    34from urlparse import urlparse 
    45from django.conf import settings 
     6from django.contrib.auth import authenticate, login 
     7from django.contrib.sessions.models import Session 
     8from django.contrib.sessions.middleware import SessionWrapper 
    59from django.core.handlers.base import BaseHandler 
    610from django.core.handlers.wsgi import WSGIRequest 
    711from django.core.signals import got_request_exception 
    812from django.dispatch import dispatcher 
    9 from django.http import urlencode, SimpleCookie 
     13from django.http import urlencode, SimpleCookie, HttpRequest 
    1014from django.test import signals 
    1115from django.utils.functional import curry 
     
    114118        self.defaults = defaults 
    115119        self.cookies = SimpleCookie() 
    116         self.session = {} 
    117120        self.exc_info = None 
    118121         
     
    124127        self.exc_info = sys.exc_info() 
    125128 
     129    def _session(self): 
     130        "Obtain the current session variables" 
     131        if 'django.contrib.sessions' in settings.INSTALLED_APPS: 
     132            cookie = self.cookies.get(settings.SESSION_COOKIE_NAME, None) 
     133            if cookie: 
     134                return SessionWrapper(cookie.value) 
     135        return {} 
     136    session = property(_session) 
     137     
    126138    def request(self, **request): 
    127139        """ 
     
    172184            raise self.exc_info[1], None, self.exc_info[2] 
    173185         
    174         # Update persistent cookie and session data 
     186        # Update persistent cookie data 
    175187        if response.cookies: 
    176188            self.cookies.update(response.cookies) 
    177189 
    178         if 'django.contrib.sessions' in settings.INSTALLED_APPS: 
    179             from django.contrib.sessions.middleware import SessionWrapper 
    180             cookie = self.cookies.get(settings.SESSION_COOKIE_NAME, None) 
    181             if cookie: 
    182                 self.session = SessionWrapper(cookie.value) 
    183              
    184190        return response 
    185191 
     
    216222        return self.request(**r) 
    217223 
    218     def login(self, path, username, password, **extra): 
    219         """ 
    220         A specialized sequence of GET and POST to log into a view that 
    221         is protected by a @login_required access decorator. 
    222  
    223         path should be the URL of the page that is login protected. 
    224  
    225         Returns the response from GETting the requested URL after 
    226         login is complete. Returns False if login process failed. 
    227         """ 
    228         # First, GET the page that is login protected. 
    229         # This page will redirect to the login page. 
    230         response = self.get(path) 
    231         if response.status_code != 302: 
     224    def login(self, **credentials): 
     225        """Set the Client to appear as if it has sucessfully logged into a site. 
     226 
     227        Returns True if login is possible; False if the provided credentials 
     228        are incorrect, or if the Sessions framework is not available. 
     229        """ 
     230        user = authenticate(**credentials) 
     231        if user and 'django.contrib.sessions' in settings.INSTALLED_APPS: 
     232            obj = Session.objects.get_new_session_object() 
     233 
     234            # Create a fake request to store login details 
     235            request = HttpRequest() 
     236            request.session = SessionWrapper(obj.session_key) 
     237            login(request, user) 
     238 
     239            # Set the cookie to represent the session 
     240            self.cookies[settings.SESSION_COOKIE_NAME] = obj.session_key 
     241            self.cookies[settings.SESSION_COOKIE_NAME]['max-age'] = None 
     242            self.cookies[settings.SESSION_COOKIE_NAME]['path'] = '/' 
     243            self.cookies[settings.SESSION_COOKIE_NAME]['domain'] = settings.SESSION_COOKIE_DOMAIN 
     244            self.cookies[settings.SESSION_COOKIE_NAME]['secure'] = settings.SESSION_COOKIE_SECURE or None 
     245            self.cookies[settings.SESSION_COOKIE_NAME]['expires'] = None 
     246 
     247            # Set the session values 
     248            Session.objects.save(obj.session_key, request.session._session, 
     249                datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE))         
     250 
     251            return True 
     252        else: 
    232253            return False 
    233  
    234         _, _, login_path, _, data, _= urlparse(response['Location']) 
    235         next = data.split('=')[1] 
    236  
    237         # Second, GET the login page; required to set up cookies 
    238         response = self.get(login_path, **extra) 
    239         if response.status_code != 200: 
    240             return False 
    241  
    242         # Last, POST the login data. 
    243         form_data = { 
    244             'username': username, 
    245             'password': password, 
    246             'next' : next, 
    247         } 
    248         response = self.post(login_path, data=form_data, **extra) 
    249  
    250         # Login page should 302 redirect to the originally requested page 
    251         if (response.status_code != 302 or  
    252                 urlparse(response['Location'])[2] != path): 
    253             return False 
    254  
    255         # Since we are logged in, request the actual page again 
    256         return self.get(path) 
     254             
  • django/branches/boulder-oracle-sprint/django/test/testcases.py

    r4695 r5157  
    11import re, doctest, unittest 
     2from urlparse import urlparse 
    23from django.db import transaction 
    34from django.core import management 
    45from django.db.models import get_apps 
    5      
     6from django.test.client import Client 
     7 
    68normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s) 
    79 
     
    4749         
    4850        """ 
     51        self.client = Client() 
    4952        self.install_fixtures() 
    5053        super(TestCase, self).run(result) 
     54 
     55    def assertRedirects(self, response, expected_path): 
     56        """Assert that a response redirected to a specific URL, and that the  
     57        redirect URL can be loaded. 
     58         
     59        """ 
     60        self.assertEqual(response.status_code, 302,  
     61            "Response didn't redirect: Reponse code was %d" % response.status_code) 
     62        scheme, netloc, path, params, query, fragment = urlparse(response['Location']) 
     63        self.assertEqual(path, expected_path,  
     64            "Response redirected to '%s', expected '%s'" % (path, expected_path)) 
     65        redirect_response = self.client.get(path) 
     66        self.assertEqual(redirect_response.status_code, 200,  
     67            "Couldn't retrieve redirection page '%s'" % path) 
     68     
     69    def assertContains(self, response, text, count=1): 
     70        """Assert that a response indicates that a page was retreived successfully, 
     71        (i.e., the HTTP status code was 200), and that ``text`` occurs ``count``  
     72        times in the content of the response. 
     73         
     74        """ 
     75        self.assertEqual(response.status_code, 200, 
     76            "Couldn't retrieve page'") 
     77        real_count = response.content.count(text) 
     78        self.assertEqual(real_count, count, 
     79            "Could only find %d of %d instances of '%s' in response" % (real_count, count, text)) 
     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         
  • django/branches/boulder-oracle-sprint/docs/email.txt

    r5148 r5157  
    2121        ['to@example.com'], fail_silently=False) 
    2222 
    23 Mail will be sent using the SMTP host and port specified in the `EMAIL_HOST`_ 
    24 and `EMAIL_PORT`_ settings. The `EMAIL_HOST_USER`_ and `EMAIL_HOST_PASSWORD`_ 
    25 settings, if set, will be used to authenticate to the SMTP server and the 
    26 `EMAIL_USE_TLS`_ settings will control whether a secure connection is used. 
     23Mail is sent using the SMTP host and port specified in the `EMAIL_HOST`_ and 
     24`EMAIL_PORT`_ settings. The `EMAIL_HOST_USER`_ and `EMAIL_HOST_PASSWORD`_ 
     25settings, if set, are used to authenticate to the SMTP server, and the 
     26`EMAIL_USE_TLS`_ setting controls whether a secure connection is used. 
    2727 
    2828.. note:: 
    2929 
    30     The character set of email sent with ``django.core.mail`` will be set to 
     30    The character set of e-mail sent with ``django.core.mail`` will be set to 
    3131    the value of your `DEFAULT_CHARSET setting`_. 
    3232 
     
    3737.. _EMAIL_HOST_PASSWORD: ../settings/#email-host-password 
    3838.. _EMAIL_USE_TLS: ../settings/#email-use-tls 
    39  
    4039 
    4140send_mail() 
     
    194193Django's ``send_mail()`` and ``send_mass_mail()`` functions are actually thin 
    195194wrappers that make use of the ``EmailMessage`` and ``SMTPConnection`` classes 
    196 in ``django.mail``.  If you ever need to customize the way Django sends email, 
    197 you can subclass these two classes to suit your needs. 
     195in ``django.core.mail``.  If you ever need to customize the way Django sends 
     196e-mail, you can subclass these two classes to suit your needs. 
    198197 
    199198.. note:: 
    200199    Not all features of the ``EmailMessage`` class are available through the 
    201200    ``send_mail()`` and related wrapper functions. If you wish to use advanced 
    202     features such as including BCC recipients or multi-part email, you will 
    203     need to create ``EmailMessage`` instances directly. 
    204  
    205 In general, ``EmailMessage`` is responsible for creating the email message 
     201    features, such as BCC'ed recipients or multi-part e-mail, you'll need to 
     202    create ``EmailMessage`` instances directly. 
     203 
     204In general, ``EmailMessage`` is responsible for creating the e-mail message 
    206205itself. ``SMTPConnection`` is responsible for the network connection side of 
    207206the operation. This means you can reuse the same connection (an 
    208207``SMTPConnection`` instance) for multiple messages. 
    209208 
    210 The ``EmailMessage`` class is initialised as follows:: 
     209The ``EmailMessage`` class is initialized as follows:: 
    211210 
    212211    email = EmailMessage(subject, body, from_email, to, bcc, connection) 
     
    214213All of these parameters are optional. If ``from_email`` is omitted, the value 
    215214from ``settings.DEFAULT_FROM_EMAIL`` is used. Both the ``to`` and ``bcc`` 
    216 parameters are lists of addresses. 
    217  
    218 The class has the following methods that you can use: 
    219  
    220  * ``send()`` sends the message, using either the connection that is specified 
    221    in the ``connection`` attribute, or creating a new connection if none already 
    222    exists. 
    223  * ``message()`` constructs a ``django.core.mail.SafeMIMEText`` object (a 
    224    sub-class of Python's ``email.MIMEText.MIMEText`` class) holding the 
    225    message to be sent. If you ever need to extend the `EmailMessage` class, 
    226    you will probably want to override this method to put the content you wish 
    227    into the MIME object. 
    228  * ``recipients()`` returns a lists of all the recipients of the message, 
    229    whether they are recorded in the ``to`` or ``bcc`` attributes. This is 
    230    another method you need to possibly override when sub-classing, since the 
    231    SMTP server needs to be told the full list of recipients when the message 
    232    is sent. If you add another way to specify recipients in your class, they 
    233    need to be returned from this method as well. 
     215parameters are lists of addresses, as strings. 
     216 
     217For example:: 
     218 
     219    email = EmailMessage('Hello', 'Body goes here', 'from@example.com', 
     220                ['to1@example.com', 'to2@example.com'], 
     221                ['bcc@example.com']) 
     222 
     223The class has the following methods: 
     224 
     225    * ``send()`` sends the message, using either the connection that is 
     226      specified in the ``connection`` attribute, or creating a new connection 
     227      if none already exists. 
     228 
     229    * ``message()`` constructs a ``django.core.mail.SafeMIMEText`` object (a 
     230      sub-class of Python's ``email.MIMEText.MIMEText`` class) holding the 
     231      message to be sent. If you ever need to extend the `EmailMessage` class, 
     232      you'll probably want to override this method to put the content you wish 
     233      into the MIME object. 
     234 
     235    * ``recipients()`` returns a list of all the recipients of the message, 
     236      whether they're recorded in the ``to`` or ``bcc`` attributes. This is 
     237      another method you might need to override when sub-classing, because the 
     238      SMTP server needs to be told the full list of recipients when the message 
     239      is sent. If you add another way to specify recipients in your class, they 
     240      need to be returned from this method as well. 
    234241 
    235242The ``SMTPConnection`` class is initialized with the host, port, username and 
     
    237244options, they are read from your settings file. 
    238245 
    239 If you are sending lots of messages at once, the ``send_messages()`` method of 
    240 the ``SMTPConnection`` class will be useful. It takes a list of ``EmailMessage`` 
    241 instances (or sub-classes) and sends them over a single connection. For 
    242 example, if you have a function called ``get_notification_email()`` that returns a 
    243 list of ``EmailMessage`` objects representing some periodic email you wish to 
     246If you're sending lots of messages at once, the ``send_messages()`` method of 
     247the ``SMTPConnection`` class is useful. It takes a list of ``EmailMessage`` 
     248instances (or subclasses) and sends them over a single connection. For example, 
     249if you have a function called ``get_notification_email()`` that returns a 
     250list of ``EmailMessage`` objects representing some periodic e-mail you wish to 
    244251send out, you could send this with:: 
    245252 
     
    247254    messages = get_notification_email() 
    248255    connection.send_messages(messages) 
    249  
  • django/branches/boulder-oracle-sprint/docs/testing.txt

    r5047 r5157  
    33=========================== 
    44 
    5 Automated testing is an extremely useful weapon in the bug-killing arsenal 
    6 of the modern developer. When initially writing code, a test suite can be 
    7 used to validate that code behaves as expected. When refactoring or 
    8 modifying code, tests serve as a guide to ensure that behavior hasn't 
    9 changed unexpectedly as a result of the refactor. 
    10  
    11 Testing a web application is a complex task, as there are many 
    12 components of a web application that must be validated and tested. To 
    13 help you test your application, Django provides a test execution 
    14 framework, and range of utilities that can be used to simulate and 
    15 inspect various facets of a web application. 
    16  
    17     This testing framework is currently under development, and may change 
     5Automated testing is an extremely useful bug-killing tool for the modern 
     6Web developer. You can use a collection of tests -- a **test suite** -- to 
     7to solve, or avoid, a number of problems: 
     8 
     9    * When you're writing new code, you can use tests to validate your code 
     10      works as expected. 
     11 
     12    * When you're refactoring or modifying old code, you can use tests to 
     13      ensure your changes haven't affected your application's behavior 
     14      unexpectedly. 
     15 
     16Testing a Web application is a complex task, because a Web application is made 
     17of several layers of logic -- from HTTP-level request handling, to form 
     18validation and processing, to template rendering. With Django's test-execution 
     19framework and assorted utilities, you can simulate requests, insert test data, 
     20inspect your application's output and generally verify your code is doing what 
     21it should be doing. 
     22 
     23The best part is, it's really easy. 
     24 
     25.. admonition:: Note 
     26 
     27    This testing framework is currently under development. It may change 
    1828    slightly before the next official Django release. 
    1929 
     
    167177 
    168178* `Test Client`_ 
    169 * Fixtures
     179* `TestCase`
    170180 
    171181Test Client 
     
    217227``post(path, data={}, content_type=MULTIPART_CONTENT)`` 
    218228    Make a POST request on the provided ``path``. If you provide a content type 
    219     (e.g., ``text/xml`` for an XML payload), the contents of ``data`` will be  
    220     sent as-is in the POST request, using the content type in the HTTP  
     229    (e.g., ``text/xml`` for an XML payload), the contents of ``data`` will be 
     230    sent as-is in the POST request, using the content type in the HTTP 
    221231    ``Content-Type`` header. 
    222      
    223     If you do not provide a value for ``content_type``, the values in  
     232 
     233    If you do not provide a value for ``content_type``, the values in 
    224234    ``data`` will be transmitted with a content type of ``multipart/form-data``. 
    225235    The key-value pairs in the data dictionary will be encoded as a multipart 
    226236    message and used to create the POST data payload. 
    227      
    228     To submit multiple values for a given key (for example, to specify  
    229     the selections for a multiple selection list), provide the values as a  
     237 
     238    To submit multiple values for a given key (for example, to specify 
     239    the selections for a multiple selection list), provide the values as a 
    230240    list or tuple for the required key. For example, a data dictionary of 
    231241    ``{'choices': ('a','b','d')}`` would submit three selected rows for the 
    232242    field named ``choices``. 
    233      
     243 
    234244    Submitting files is a special case. To POST a file, you need only 
    235245    provide the file field name as a key, and a file handle to the file you wish to 
     
    247257    need to manually close the file after it has been provided to the POST. 
    248258 
    249 ``login(path, username, password)`` 
    250     In a production site, it is likely that some views will be protected with 
    251     the @login_required decorator provided by ``django.contrib.auth``. Interacting 
    252     with a URL that has been login protected is a slightly complex operation, 
    253     so the Test Client provides a simple method to automate the login process. A 
    254     call to ``login()`` stimulates the series of GET and POST calls required 
    255     to log a user into a @login_required protected view. 
    256  
    257     If login is possible, the final return value of ``login()`` is the response 
    258     that is generated by issuing a GET request on the protected URL. If login 
    259     is not possible, ``login()`` returns False. 
     259``login(**credentials)`` 
     260    ** New in Django development version ** 
     261 
     262    On a production site, it is likely that some views will be protected from 
     263    anonymous access through the use of the @login_required decorator, or some 
     264    other login checking mechanism. The ``login()`` method can be used to 
     265    simulate the effect of a user logging into the site. As a result of calling 
     266    this method, the Client will have all the cookies and session data required 
     267    to pass any login-based tests that may form part of a view. 
     268 
     269    In most cases, the ``credentials`` required by this method are the username 
     270    and password of the user that wants to log in, provided as keyword 
     271    arguments:: 
     272 
     273        c = Client() 
     274        c.login(username='fred', password='secret') 
     275        # Now you can access a login protected view 
     276 
     277    If you are using a different authentication backend, this method may 
     278    require different credentials. 
     279 
     280    ``login()`` returns ``True`` if it the credentials were accepted and login 
     281    was successful. 
    260282 
    261283    Note that since the test suite will be executed using the test database, 
    262     which contains no users by default. As a result, logins for your production 
    263     site will not work. You will need to create users as part of the test suite 
    264     to be able to test logins to your application. 
     284    which contains no users by default. As a result, logins that are valid 
     285    on your production site will not work under test conditions. You will 
     286    need to create users as part of the test suite (either manually, or 
     287    using a test fixture). 
    265288 
    266289Testing Responses 
     
    358381            self.failUnlessEqual(len(response.context['customers']), 5) 
    359382 
    360 Fixtures 
     383TestCase 
    361384-------- 
     385 
     386Normal python unit tests extend a base class of ``unittest.testCase``. 
     387Django provides an extension of this base class - ``django.test.TestCase`` 
     388- that provides some additional capabilities that can be useful for 
     389testing web sites. 
     390 
     391Moving from a normal unittest TestCase to a Django TestCase is easy - just 
     392change the base class of your test from ``unittest.TestCase`` to 
     393``django.test.TestCase``. All of the standard Python unit test facilities 
     394will continue to be available, but they will be augmented with some useful 
     395extra facilities. 
     396 
     397Default Test Client 
     398~~~~~~~~~~~~~~~~~~~ 
     399** New in Django development version ** 
     400 
     401Every test case in a ``django.test.TestCase`` instance has access to an 
     402instance of a Django `Test Client`_. This Client can be accessed as 
     403``self.client``. This client is recreated for each test. 
     404 
     405Fixture loading 
     406~~~~~~~~~~~~~~~ 
    362407 
    363408A test case for a database-backed website isn't much use if there isn't any 
     
    371416 
    372417.. note:: 
    373     If you have synchronized a Django project, you have already experienced  
     418    If you have synchronized a Django project, you have already experienced 
    374419    the use of one fixture -- the ``initial_data`` fixture. Every time you 
    375420    synchronize the database, Django installs the ``initial_data`` fixture. 
    376421    This provides a mechanism to populate a new database with any initial 
    377422    data (such as a default set of categories). Fixtures with other names 
    378     can be installed manually using ``django-admin.py loaddata``.  
    379      
    380  
    381 However, for the purposes of unit testing, each test must be able to  
     423    can be installed manually using ``django-admin.py loaddata``. 
     424 
     425However, for the purposes of unit testing, each test must be able to 
    382426guarantee the contents of the database at the start of each and every 
    383 test. To do this, Django provides a TestCase baseclass that can integrate 
    384 with fixtures. 
    385  
    386 Moving from a normal unittest TestCase to a Django TestCase is easy - just 
    387 change the base class of your test, and define a list of fixtures 
    388 to be used. For example, the test case from `Writing unittests`_ would  
     427test. 
     428 
     429To define a fixture for a test, all you need to do is add a class 
     430attribute to your test describing the fixtures you want the test to use. 
     431For example, the test case from `Writing unittests`_ would 
    389432look like:: 
    390433 
     
    394437    class AnimalTestCase(TestCase): 
    395438        fixtures = ['mammals.json', 'birds'] 
    396          
     439 
    397440        def setUp(self): 
    398441            # test definitions as before 
    399442 
    400443At the start of each test case, before ``setUp()`` is run, Django will 
    401 flush the database, returning the database the state it was in directly  
    402 after ``syncdb`` was called. Then, all the named fixtures are installed.  
     444flush the database, returning the database the state it was in directly 
     445after ``syncdb`` was called. Then, all the named fixtures are installed. 
    403446In this example, any JSON fixture called ``mammals``, and any fixture 
    404 named ``birds`` will be installed. See the documentation on  
     447named ``birds`` will be installed. See the documentation on 
    405448`loading fixtures`_ for more details on defining and installing fixtures. 
    406449 
    407450.. _`loading fixtures`: ../django-admin/#loaddata-fixture-fixture 
    408451 
    409 This flush/load procedure is repeated for each test in the test case, so you  
    410 can be certain that the outcome of a test will not be affected by  
     452This flush/load procedure is repeated for each test in the test case, so you 
     453can be certain that the outcome of a test will not be affected by 
    411454another test, or the order of test execution. 
    412455 
     456Assertions 
     457~~~~~~~~~~ 
     458** New in Django development version ** 
     459 
     460Normal Python unit tests have a wide range of assertions, such as 
     461``assertTrue`` and ``assertEquals`` that can be used to validate behavior. 
     462``django.TestCase`` adds to these, providing some assertions 
     463that can be useful in testing the behavior of web sites. 
     464 
     465``assertContains(response, text, count=1)`` 
     466    Assert that a response indicates that a page was retrieved successfully, 
     467    (i.e., the HTTP status code was 200), and that ``text`` occurs ``count`` 
     468    times in the content of the response. 
     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     
    413495Running tests 
    414496============= 
     
    469551    FAILED (failures=1) 
    470552 
    471 The return code for the script is the total number of failed and erroneous  
     553The return code for the script is the total number of failed and erroneous 
    472554tests. If all the tests pass, the return code is 0. 
    473555 
    474556Regardless of whether the tests pass or fail, the test database is destroyed when 
    475 all the tests have been executed.  
     557all the tests have been executed. 
    476558 
    477559Using a different testing framework 
     
    484566 
    485567When you run ``./manage.py test``, Django looks at the ``TEST_RUNNER`` 
    486 setting to determine what to do. By default, ``TEST_RUNNER`` points to  
     568setting to determine what to do. By default, ``TEST_RUNNER`` points to 
    487569``django.test.simple.run_tests``. This method defines the default Django 
    488570testing behavior. This behavior involves: 
     
    514596    will be printed to the console; `0` is no output, `1` is normal output, 
    515597    and `2` is verbose output. 
    516      
     598 
    517599    This method should return the number of tests that failed. 
    518600 
  • django/branches/boulder-oracle-sprint/tests/modeltests/custom_columns/models.py

    r5135 r5157  
    7272Traceback (most recent call last): 
    7373    ... 
    74 TypeError: Cannot resolve keyword 'firstname' into field, choices are: article, id, first_name, last_name 
     74TypeError: Cannot resolve keyword 'firstname' into field. Choices are: article, id, first_name, last_name 
    7575 
    7676>>> a = Author.objects.get(last_name__exact='Smith') 
  • django/branches/boulder-oracle-sprint/tests/modeltests/lookup/models.py

    r5135 r5157  
    224224Traceback (most recent call last): 
    225225    ... 
    226 TypeError: Cannot resolve keyword 'pub_date_year' into field, choices are: id, headline, pub_date 
     226TypeError: Cannot resolve keyword 'pub_date_year' into field. Choices are: id, headline, pub_date 
    227227 
    228228>>> Article.objects.filter(headline__starts='Article') 
    229229Traceback (most recent call last): 
    230230    ... 
    231 TypeError: Cannot resolve keyword 'headline__starts' into field, choices are: id, headline, pub_date 
     231TypeError: Cannot resolve keyword 'headline__starts' into field. Choices are: id, headline, pub_date 
    232232 
    233233"""} 
  • django/branches/boulder-oracle-sprint/tests/modeltests/many_to_one/models.py

    r5135 r5157  
    175175Traceback (most recent call last): 
    176176    ... 
    177 TypeError: Cannot resolve keyword 'reporter_id' into field, choices are: id, headline, pub_date, reporter 
     177TypeError: Cannot resolve keyword 'reporter_id' into field. Choices are: id, headline, pub_date, reporter 
    178178 
    179179# You need to specify a comparison clause 
     
    181181Traceback (most recent call last): 
    182182    ... 
    183 TypeError: Cannot resolve keyword 'reporter_id' into field, choices are: id, headline, pub_date, reporter 
     183TypeError: Cannot resolve keyword 'reporter_id' into field. Choices are: id, headline, pub_date, reporter 
    184184 
    185185# You can also instantiate an Article by passing 
  • django/branches/boulder-oracle-sprint/tests/modeltests/reverse_lookup/models.py

    r5135 r5157  
    5656Traceback (most recent call last): 
    5757    ... 
    58 TypeError: Cannot resolve keyword 'choice' into field, choices are: poll_choice, related_choice, id, question, creator 
     58TypeError: Cannot resolve keyword 'choice' into field. Choices are: poll_choice, related_choice, id, question, creator 
    5959"""} 
  • django/branches/boulder-oracle-sprint/tests/modeltests/test_client/models.py

    r4841 r5157  
    2525    fixtures = ['testdata.json'] 
    2626     
    27     def setUp(self): 
    28         "Set up test environment" 
    29         self.client = Client() 
    30          
    3127    def test_get_view(self): 
    3228        "GET a view" 
     
    3430         
    3531        # Check some response details 
    36         self.assertEqual(response.status_code, 200
     32        self.assertContains(response, 'This is a test'
    3733        self.assertEqual(response.context['var'], 42) 
    3834        self.assertEqual(response.template.name, 'GET Template') 
    39         self.failUnless('This is a test.' in response.content) 
    40  
     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         
    4143    def test_get_post_view(self): 
    4244        "GET a view that normally expects POSTs" 
     
    4648        self.assertEqual(response.status_code, 200) 
    4749        self.assertEqual(response.template.name, 'Empty GET Template') 
     50        self.assertTemplateUsed(response, 'Empty GET Template') 
     51        self.assertTemplateNotUsed(response, 'Empty POST Template') 
    4852         
    4953    def test_empty_post(self): 
     
    5458        self.assertEqual(response.status_code, 200) 
    5559        self.assertEqual(response.template.name, 'Empty POST Template') 
     60        self.assertTemplateNotUsed(response, 'Empty GET Template') 
     61        self.assertTemplateUsed(response, 'Empty POST Template') 
    5662         
    5763    def test_post(self): 
     
    8187         
    8288        # Check that the response was a 302 (redirect) 
    83         self.assertEqual(response.status_code, 302
     89        self.assertRedirects(response, '/test_client/get_view/'
    8490 
    8591    def test_valid_form(self): 
     
    94100        response = self.client.post('/test_client/form_view/', post_data) 
    95101        self.assertEqual(response.status_code, 200) 
    96         self.assertEqual(response.template.name, "Valid POST Template") 
     102        self.assertTemplateUsed(response, "Valid POST Template") 
    97103 
    98104    def test_incomplete_data_form(self): 
     
    103109        } 
    104110        response = self.client.post('/test_client/form_view/', post_data) 
    105         self.assertEqual(response.status_code, 200) 
    106         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.') 
    107118 
    108119    def test_form_error(self): 
     
    117128        response = self.client.post('/test_client/form_view/', post_data) 
    118129        self.assertEqual(response.status_code, 200) 
    119         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.') 
    120181         
    121182    def test_unknown_page(self): 
     
    131192        # Get the page without logging in. Should result in 302. 
    132193        response = self.client.get('/test_client/login_protected_view/') 
    133         self.assertEqual(response.status_code, 302) 
    134          
     194        self.assertRedirects(response, '/accounts/login/') 
     195         
     196        # Log in 
     197        self.client.login(username='testclient', password='password') 
     198 
    135199        # Request a page that requires a login 
    136         response = self.client.login('/test_client/login_protected_view/', 'testclient', 'password') 
    137         self.failUnless(response) 
     200        response = self.client.get('/test_client/login_protected_view/') 
    138201        self.assertEqual(response.status_code, 200) 
    139202        self.assertEqual(response.context['user'].username, 'testclient') 
    140         self.assertEqual(response.template.name, 'Login Template') 
    141203 
    142204    def test_view_with_bad_login(self): 
    143205        "Request a page that is protected with @login, but use bad credentials" 
    144206 
    145         response = self.client.login('/test_client/login_protected_view/', 'otheruser', 'nopassword') 
    146         self.failIf(response
     207