summaryrefslogtreecommitdiff
path: root/docs/testing.txt
diff options
context:
space:
mode:
authorJason Pellerin <jpellerin@gmail.com>2006-09-08 16:35:39 +0000
committerJason Pellerin <jpellerin@gmail.com>2006-09-08 16:35:39 +0000
commit84f7a2133c4d553a234165bb8cbfaf70681bb028 (patch)
tree53d08c1ffc275a4e1d62d7f48ea81172a5919051 /docs/testing.txt
parentae3896cb74d4bc42acaf6fade0d2a57e28045b2a (diff)
[multi-db] Merge trunk to [3737]. Some tests still failing.
git-svn-id: http://code.djangoproject.com/svn/django/branches/multiple-db-support@3739 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'docs/testing.txt')
-rw-r--r--docs/testing.txt210
1 files changed, 180 insertions, 30 deletions
diff --git a/docs/testing.txt b/docs/testing.txt
index 98ed1e8aec..b1ede3e4cc 100644
--- a/docs/testing.txt
+++ b/docs/testing.txt
@@ -92,7 +92,8 @@ Writing unittests
Like doctests, Django's unit tests use a standard library module: unittest_.
As with doctests, Django's test runner looks for any unit test cases defined
-in ``models.py``, or in a ``tests.py`` file in your application directory.
+in ``models.py``, or in a ``tests.py`` file stored in the application
+directory.
An equivalent unittest test case for the above example would look like::
@@ -110,8 +111,9 @@ An equivalent unittest test case for the above example would look like::
self.assertEquals(self.cat.speak(), 'The cat says "meow"')
When you `run your tests`_, the test utility will find all the test cases
-(that is, subclasses of ``unittest.TestCase``) in ``tests.py``, automatically
-build a test suite out of those test cases, and run that suite.
+(that is, subclasses of ``unittest.TestCase``) in ``models.py`` and
+``tests.py``, automatically build a test suite out of those test cases,
+and run that suite.
For more details about ``unittest``, see the `standard library unittest
documentation`_.
@@ -159,20 +161,164 @@ Again, remember that you can use both systems side-by-side (even in the same
app). In the end, most projects will eventually end up using both; each shines
in different circumstances.
-Testing utilities
-=================
+Testing Tools
+=============
+
+To assist in testing various features of your application, Django provides
+tools that can be used to establish tests and test conditions.
+* `Test Client`_
+* Fixtures_
+
Test Client
-----------
-A dummy browser; instruments the template generation process...
+The Test Client is a simple dummy browser. It allows you to simulate
+GET and POST requests on a URL, and observe the response that is received.
+This allows you to test that the correct view is executed for a given URL,
+and that the view constructs the correct response.
+
+As the response is generated, the Test Client gathers details on the
+Template and Context objects that were used to generate the response. These
+Templates and Contexts are then provided as part of the response, and can be
+used as test conditions.
+
+.. admonition:: Test Client vs Browser Automation?
+
+ The Test Client is not intended as a replacement for Twill_, Selenium_,
+ or other browser automation frameworks - it is intended to allow
+ testing of the contexts and templates produced by a view,
+ rather than the HTML rendered to the end-user.
+
+ A comprehensive test suite should use a combination of both: Test Client
+ tests to establish that the correct view is being called and that
+ the view is collecting the correct context data, and Browser Automation
+ tests to check that user interface behaves as expected.
+
+.. _Twill: http://twill.idyll.org/
+.. _Selenium: http://www.openqa.org/selenium/
+
+The Test Client is stateful; if a cookie is returned as part of a response,
+that cookie is provided as part of the next request issued to that Client
+instance. Expiry policies for these cookies are not followed; if you want
+a cookie to expire, either delete it manually from ``client.cookies``, or
+create a new Client instance (which will effectively delete all cookies).
+
+Making requests
+~~~~~~~~~~~~~~~
+
+Creating an instance of ``Client`` (``django.test.client.Client``) requires
+no arguments at time of construction. Once constructed, the following methods
+can be invoked on the ``Client`` instance.
+
+``get(path, data={})``
+ Make a GET request on the provided ``path``. The key-value pairs in the
+ data dictionary will be used to create a GET data payload. For example::
+
+ c = Client()
+ c.get('/customers/details/', {'name':'fred', 'age':7})
+
+ will result in the evaluation of a GET request equivalent to::
+
+ http://yoursite.com/customers/details/?name='fred'&age=7
+
+``post(path, data={})``
+ Make a POST request on the provided ``path``. The key-value pairs in the
+ data dictionary will be used to create the POST data payload. This payload
+ will be transmitted with the mimetype ``multipart/form-data``.
+
+ However submitting files is a special case. To POST a file, you need only
+ provide the file field name as a key, and a file handle to the file you wish to
+ upload as a value. The Test Client will populate the two POST fields (i.e.,
+ ``field`` and ``field_file``) required by FileField. For example::
+
+ c = Client()
+ f = open('wishlist.doc')
+ c.post('/customers/wishes/', {'name':'fred', 'attachment':f})
+ f.close()
+
+ will result in the evaluation of a POST request on ``/customers/wishes/``,
+ with a POST dictionary that contains `name`, `attachment` (containing the
+ file name), and `attachment_file` (containing the file data). Note that you
+ need to manually close the file after it has been provided to the POST.
+
+``login(path, username, password)``
+ In a production site, it is likely that some views will be protected with
+ the @login_required URL provided by ``django.contrib.auth``. Interacting
+ with a URL that has been login protected is a slightly complex operation,
+ so the Test Client provides a simple URL to automate the login process. A
+ call to ``login()`` stimulates the series of GET and POST calls required
+ to log a user into a @login_required protected URL.
+
+ If login is possible, the final return value of ``login()`` is the response
+ that is generated by issuing a GET request on the protected URL. If login
+ is not possible, ``login()`` returns False.
+
+ Note that since the test suite will be executed using the test database,
+ which contains no users by default. As a result, logins for your production
+ site will not work. You will need to create users as part of the test suite
+ to be able to test logins to your application.
+
+Testing Responses
+~~~~~~~~~~~~~~~~~
+
+The ``get()``, ``post()`` and ``login()`` methods all return a Response
+object. This Response object has the following properties that can be used
+for testing purposes:
+
+ =============== ==========================================================
+ Property Description
+ =============== ==========================================================
+ ``status_code`` The HTTP status of the response. See RFC2616_ for a
+ full list of HTTP status codes.
+
+ ``content`` The body of the response. The is the final page
+ content as rendered by the view, or any error message
+ (such as the URL for a 302 redirect).
+
+ ``template`` The Template instance that was used to render the final
+ content. Testing ``template.name`` can be particularly
+ useful; if the template was loaded from a file,
+ ``template.name`` will be the file name that was loaded.
+
+ If multiple templates were rendered, (e.g., if one
+ template includes another template),``template`` will
+ be a list of Template objects, in the order in which
+ they were rendered.
+
+ ``context`` The Context that was used to render the template that
+ produced the response content.
+
+ As with ``template``, if multiple templates were rendered
+ ``context`` will be a list of Context objects, stored in
+ the order in which they were rendered.
+ =============== ==========================================================
+
+.. _RFC2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+
+The following is a simple unit test using the Test Client::
+
+ import unittest
+ from django.test.client import Client
+
+ class SimpleTest(unittest.TestCase):
+ def setUp(self):
+ # Every test needs a client
+ self.client = Client()
+ def test_details(self):
+ # Issue a GET request
+ response = self.client.get('/customer/details/')
+
+ # Check that the respose is 200 OK
+ self.failUnlessEqual(response.status_code, 200)
+ # Check that the rendered context contains 5 customers
+ self.failUnlessEqual(len(response.context['customers']), 5)
Fixtures
--------
Feature still to come...
-
Running tests
=============
@@ -245,11 +391,13 @@ When you run ``./manage.py test``, Django looks at the ``TEST_RUNNER``
setting to determine what to do. By default, ``TEST_RUNNER`` points to ``django.test.simple.run_tests``. This method defines the default Django
testing behaviour. This behaviour involves:
+#. Performing global pre-test setup
#. Creating the test database
#. Running ``syncdb`` to install models and initial data into the test database
#. Looking for Unit Tests and Doctests in ``models.py`` and ``tests.py`` file for each installed application
#. Running the Unit Tests and Doctests that are found
#. Destroying the test database.
+#. Performing global post-test teardown
If you define your own test runner method and point ``TEST_RUNNER``
at that method, Django will execute your test runner whenever you run
@@ -263,14 +411,12 @@ can call it anything you want. The only requirement is that it accept two
arguments:
``run_tests(module_list, verbosity=1)``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The module list is the list of Python modules that contain the models to be
-tested. This is the same format returned by ``django.db.models.get_apps()``
+ The module list is the list of Python modules that contain the models to be
+ tested. This is the same format returned by ``django.db.models.get_apps()``
-Verbosity determines the amount of notification and debug information that
-will be printed to the console; '0' is no output, '1' is normal output,
-and `2` is verbose output.
+ Verbosity determines the amount of notification and debug information that
+ will be printed to the console; '0' is no output, '1' is normal output,
+ and `2` is verbose output.
Testing utilities
-----------------
@@ -278,26 +424,30 @@ Testing utilities
To assist in the creation of your own test runner, Django provides
a number of utility methods in the ``django.test.utils`` module.
-``create_test_db(verbosity=1, autoclobber=False)``:
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+``setup_test_environment()``
+ Performs any global pre-test setup, such as the installing the
+ instrumentation of the template rendering system.
-Creates a new test database, and run ``syncdb`` against it.
+``teardown_test_environment()``
+ Performs any global post-test teardown, such as removing the instrumentation
+ of the template rendering system.
-``verbosity`` has the same behaviour as in the test runner.
+``create_test_db(verbosity=1, autoclobber=False)``
+ Creates a new test database, and run ``syncdb`` against it.
-``Autoclobber`` describes the behavior that will occur if a database with
-the same name as the test database is discovered. If ``autoclobber`` is False,
-the user will be asked to approve destroying the existing database. ``sys.exit``
-is called if the user does not approve. If autoclobber is ``True``, the database
-will be destroyed without consulting the user.
+ ``verbosity`` has the same behaviour as in the test runner.
-``create_test_db()`` has the side effect of modifying
-``settings.DATABASE_NAME`` to match the name of the test database.
+ ``Autoclobber`` describes the behavior that will occur if a database with
+ the same name as the test database is discovered. If ``autoclobber`` is False,
+ the user will be asked to approve destroying the existing database. ``sys.exit``
+ is called if the user does not approve. If autoclobber is ``True``, the database
+ will be destroyed without consulting the user.
-``destroy_test_db(old_database_name, verbosity=1)``:
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ``create_test_db()`` has the side effect of modifying
+ ``settings.DATABASE_NAME`` to match the name of the test database.
-Destroys the database with the name ``settings.DATABASE_NAME`` matching,
-and restores the value of ``settings.DATABASE_NAME`` to the provided name.
+``destroy_test_db(old_database_name, verbosity=1)``
+ Destroys the database with the name ``settings.DATABASE_NAME`` matching,
+ and restores the value of ``settings.DATABASE_NAME`` to the provided name.
-``verbosity`` has the same behaviour as in the test runner.
+ ``verbosity`` has the same behaviour as in the test runner.