summaryrefslogtreecommitdiff
path: root/docs/testing.txt
diff options
context:
space:
mode:
authorBoulder Sprinters <boulder-sprinters@djangoproject.com>2007-05-07 15:50:55 +0000
committerBoulder Sprinters <boulder-sprinters@djangoproject.com>2007-05-07 15:50:55 +0000
commita275d3da8ed8cea8c2c92fc15151f43fb56b42ce (patch)
tree9f9a7c291956a17587b898772c5949b7a44864cd /docs/testing.txt
parent0f22c6a7c8a089e2381a058b4c472cf92699950e (diff)
boulder-oracle-sprint: Merged to [5156]
git-svn-id: http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint@5157 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'docs/testing.txt')
-rw-r--r--docs/testing.txt188
1 files changed, 135 insertions, 53 deletions
diff --git a/docs/testing.txt b/docs/testing.txt
index a135734144..b3b33e9678 100644
--- a/docs/testing.txt
+++ b/docs/testing.txt
@@ -2,19 +2,29 @@
Testing Django applications
===========================
-Automated testing is an extremely useful weapon in the bug-killing arsenal
-of the modern developer. When initially writing code, a test suite can be
-used to validate that code behaves as expected. When refactoring or
-modifying code, tests serve as a guide to ensure that behavior hasn't
-changed unexpectedly as a result of the refactor.
+Automated testing is an extremely useful bug-killing tool for the modern
+Web developer. You can use a collection of tests -- a **test suite** -- to
+to solve, or avoid, a number of problems:
-Testing a web application is a complex task, as there are many
-components of a web application that must be validated and tested. To
-help you test your application, Django provides a test execution
-framework, and range of utilities that can be used to simulate and
-inspect various facets of a web application.
+ * When you're writing new code, you can use tests to validate your code
+ works as expected.
- This testing framework is currently under development, and may change
+ * When you're refactoring or modifying old code, you can use tests to
+ ensure your changes haven't affected your application's behavior
+ unexpectedly.
+
+Testing a Web application is a complex task, because a Web application is made
+of several layers of logic -- from HTTP-level request handling, to form
+validation and processing, to template rendering. With Django's test-execution
+framework and assorted utilities, you can simulate requests, insert test data,
+inspect your application's output and generally verify your code is doing what
+it should be doing.
+
+The best part is, it's really easy.
+
+.. admonition:: Note
+
+ This testing framework is currently under development. It may change
slightly before the next official Django release.
(That's *no* excuse not to write tests, though!)
@@ -166,7 +176,7 @@ 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_
+* `TestCase`_
Test Client
-----------
@@ -216,21 +226,21 @@ can be invoked on the ``Client`` instance.
``post(path, data={}, content_type=MULTIPART_CONTENT)``
Make a POST request on the provided ``path``. If you provide a content type
- (e.g., ``text/xml`` for an XML payload), the contents of ``data`` will be
- sent as-is in the POST request, using the content type in the HTTP
+ (e.g., ``text/xml`` for an XML payload), the contents of ``data`` will be
+ sent as-is in the POST request, using the content type in the HTTP
``Content-Type`` header.
-
- If you do not provide a value for ``content_type``, the values in
+
+ If you do not provide a value for ``content_type``, the values in
``data`` will be transmitted with a content type of ``multipart/form-data``.
The key-value pairs in the data dictionary will be encoded as a multipart
message and used to create the POST data payload.
-
- To submit multiple values for a given key (for example, to specify
- the selections for a multiple selection list), provide the values as a
+
+ To submit multiple values for a given key (for example, to specify
+ the selections for a multiple selection list), provide the values as a
list or tuple for the required key. For example, a data dictionary of
``{'choices': ('a','b','d')}`` would submit three selected rows for the
field named ``choices``.
-
+
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.,
@@ -246,22 +256,35 @@ can be invoked on the ``Client`` instance.
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 decorator 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 method 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 view.
+``login(**credentials)``
+ ** New in Django development version **
- 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.
+ On a production site, it is likely that some views will be protected from
+ anonymous access through the use of the @login_required decorator, or some
+ other login checking mechanism. The ``login()`` method can be used to
+ simulate the effect of a user logging into the site. As a result of calling
+ this method, the Client will have all the cookies and session data required
+ to pass any login-based tests that may form part of a view.
+
+ In most cases, the ``credentials`` required by this method are the username
+ and password of the user that wants to log in, provided as keyword
+ arguments::
+
+ c = Client()
+ c.login(username='fred', password='secret')
+ # Now you can access a login protected view
+
+ If you are using a different authentication backend, this method may
+ require different credentials.
+
+ ``login()`` returns ``True`` if it the credentials were accepted and login
+ was successful.
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.
+ which contains no users by default. As a result, logins that are valid
+ on your production site will not work under test conditions. You will
+ need to create users as part of the test suite (either manually, or
+ using a test fixture).
Testing Responses
~~~~~~~~~~~~~~~~~
@@ -357,9 +380,31 @@ The following is a simple unit test using the Test Client::
# Check that the rendered context contains 5 customers
self.failUnlessEqual(len(response.context['customers']), 5)
-Fixtures
+TestCase
--------
+Normal python unit tests extend a base class of ``unittest.testCase``.
+Django provides an extension of this base class - ``django.test.TestCase``
+- that provides some additional capabilities that can be useful for
+testing web sites.
+
+Moving from a normal unittest TestCase to a Django TestCase is easy - just
+change the base class of your test from ``unittest.TestCase`` to
+``django.test.TestCase``. All of the standard Python unit test facilities
+will continue to be available, but they will be augmented with some useful
+extra facilities.
+
+Default Test Client
+~~~~~~~~~~~~~~~~~~~
+** New in Django development version **
+
+Every test case in a ``django.test.TestCase`` instance has access to an
+instance of a Django `Test Client`_. This Client can be accessed as
+``self.client``. This client is recreated for each test.
+
+Fixture loading
+~~~~~~~~~~~~~~~
+
A test case for a database-backed website isn't much use if there isn't any
data in the database. To make it easy to put test data into the database,
Django provides a fixtures framework.
@@ -370,22 +415,20 @@ comprise the fixture can be distributed over multiple directories, in
multiple applications.
.. note::
- If you have synchronized a Django project, you have already experienced
+ If you have synchronized a Django project, you have already experienced
the use of one fixture -- the ``initial_data`` fixture. Every time you
synchronize the database, Django installs the ``initial_data`` fixture.
This provides a mechanism to populate a new database with any initial
data (such as a default set of categories). Fixtures with other names
- can be installed manually using ``django-admin.py loaddata``.
-
+ can be installed manually using ``django-admin.py loaddata``.
-However, for the purposes of unit testing, each test must be able to
+However, for the purposes of unit testing, each test must be able to
guarantee the contents of the database at the start of each and every
-test. To do this, Django provides a TestCase baseclass that can integrate
-with fixtures.
+test.
-Moving from a normal unittest TestCase to a Django TestCase is easy - just
-change the base class of your test, and define a list of fixtures
-to be used. For example, the test case from `Writing unittests`_ would
+To define a fixture for a test, all you need to do is add a class
+attribute to your test describing the fixtures you want the test to use.
+For example, the test case from `Writing unittests`_ would
look like::
from django.test import TestCase
@@ -393,23 +436,62 @@ look like::
class AnimalTestCase(TestCase):
fixtures = ['mammals.json', 'birds']
-
+
def setUp(self):
# test definitions as before
At the start of each test case, before ``setUp()`` is run, Django will
-flush the database, returning the database the state it was in directly
-after ``syncdb`` was called. Then, all the named fixtures are installed.
+flush the database, returning the database the state it was in directly
+after ``syncdb`` was called. Then, all the named fixtures are installed.
In this example, any JSON fixture called ``mammals``, and any fixture
-named ``birds`` will be installed. See the documentation on
+named ``birds`` will be installed. See the documentation on
`loading fixtures`_ for more details on defining and installing fixtures.
.. _`loading fixtures`: ../django-admin/#loaddata-fixture-fixture
-This flush/load procedure is repeated for each test in the test case, so you
-can be certain that the outcome of a test will not be affected by
+This flush/load procedure is repeated for each test in the test case, so you
+can be certain that the outcome of a test will not be affected by
another test, or the order of test execution.
+Assertions
+~~~~~~~~~~
+** New in Django development version **
+
+Normal Python unit tests have a wide range of assertions, such as
+``assertTrue`` and ``assertEquals`` that can be used to validate behavior.
+``django.TestCase`` adds to these, providing some assertions
+that can be useful in testing the behavior of web sites.
+
+``assertContains(response, text, count=1)``
+ Assert that a response indicates that a page was retrieved successfully,
+ (i.e., the HTTP status code was 200), and that ``text`` occurs ``count``
+ times in the content of the response.
+
+``assertFormError(response, form, field, errors)``
+ Assert that a field on a form raised the provided list of errors when
+ rendered on the form.
+
+ ``form`` is the name the form object was given in the template context.
+
+ ``field`` is the name of the field on the form to check. If ``field``
+ has a value of ``None``, non-field errors will be checked.
+
+ ``errors`` is an error string, or a list of error strings, that are
+ expected as a result of form validation.
+
+``assertTemplateNotUsed(response, template_name)``
+ Assert that the template with the given name was *not* used in rendering
+ the response.
+
+``assertRedirects(response, expected_path)``
+ Assert that the response received redirects the browser to the provided
+ path, and that the expected_path can be retrieved.
+
+``assertTemplateUsed(response, template_name)``
+ Assert that the template with the given name was used in rendering the
+ response.
+
+
Running tests
=============
@@ -468,11 +550,11 @@ failed::
FAILED (failures=1)
-The return code for the script is the total number of failed and erroneous
+The return code for the script is the total number of failed and erroneous
tests. If all the tests pass, the return code is 0.
Regardless of whether the tests pass or fail, the test database is destroyed when
-all the tests have been executed.
+all the tests have been executed.
Using a different testing framework
===================================
@@ -483,7 +565,7 @@ it does provide a mechanism to allow you to invoke tests constructed for
an alternative framework as if they were normal Django tests.
When you run ``./manage.py test``, Django looks at the ``TEST_RUNNER``
-setting to determine what to do. By default, ``TEST_RUNNER`` points to
+setting to determine what to do. By default, ``TEST_RUNNER`` points to
``django.test.simple.run_tests``. This method defines the default Django
testing behavior. This behavior involves:
@@ -513,7 +595,7 @@ arguments:
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.
-
+
This method should return the number of tests that failed.
Testing utilities