You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1117 lines
42 KiB
1117 lines
42 KiB
.. _topics-testing: |
|
|
|
=========================== |
|
Testing Django applications |
|
=========================== |
|
|
|
.. module:: django.test |
|
:synopsis: Testing tools for Django applications. |
|
|
|
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 |
|
solve, or avoid, a number of problems: |
|
|
|
* When you're writing new code, you can use tests to validate your code |
|
works as expected. |
|
|
|
* 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. |
|
|
|
This document is split into two primary sections. First, we explain how to |
|
write tests with Django. Then, we explain how to run them. |
|
|
|
Writing tests |
|
============= |
|
|
|
There are two primary ways to write tests with Django, corresponding to the |
|
two test frameworks that ship in the Python standard library. The two |
|
frameworks are: |
|
|
|
* **Doctests** -- tests that are embedded in your functions' docstrings and |
|
are written in a way that emulates a session of the Python interactive |
|
interpreter. For example:: |
|
|
|
def my_func(a_list, idx): |
|
""" |
|
>>> a = ['larry', 'curly', 'moe'] |
|
>>> my_func(a, 0) |
|
'larry' |
|
>>> my_func(a, 1) |
|
'curly' |
|
""" |
|
return a_list[idx] |
|
|
|
* **Unit tests** -- tests that are expressed as methods on a Python class |
|
that subclasses ``unittest.TestCase``. For example:: |
|
|
|
import unittest |
|
|
|
class MyFuncTestCase(unittest.TestCase): |
|
def testBasic(self): |
|
a = ['larry', 'curly', 'moe'] |
|
self.assertEquals(my_func(a, 0), 'larry') |
|
self.assertEquals(my_func(a, 1), 'curly') |
|
|
|
You can choose the test framework you like, depending on which syntax you |
|
prefer, or you can mix and match, using one framework for some of your code and |
|
the other framework for other code. You can also use any *other* Python test |
|
frameworks, as we'll explain in a bit. |
|
|
|
Writing doctests |
|
---------------- |
|
|
|
Doctests use Python's standard doctest_ module, which searches your docstrings |
|
for statements that resemble a session of the Python interactive interpreter. |
|
A full explanation of how doctest works is out of the scope of this document; |
|
read Python's official documentation for the details. |
|
|
|
.. admonition:: What's a **docstring**? |
|
|
|
A good explanation of docstrings (and some guidelines for using them |
|
effectively) can be found in :pep:`257`: |
|
|
|
A docstring is a string literal that occurs as the first statement in |
|
a module, function, class, or method definition. Such a docstring |
|
becomes the ``__doc__`` special attribute of that object. |
|
|
|
For example, this function has a docstring that describes what it does:: |
|
|
|
def add_two(num): |
|
"Return the result of adding two to the provided number." |
|
return num + 2 |
|
|
|
Because tests often make great documentation, putting tests directly in |
|
your docstrings is an effective way to document *and* test your code. |
|
|
|
For a given Django application, the test runner looks for doctests in two |
|
places: |
|
|
|
* The ``models.py`` file. You can define module-level doctests and/or a |
|
doctest for individual models. It's common practice to put |
|
application-level doctests in the module docstring and model-level |
|
doctests in the model docstrings. |
|
|
|
* A file called ``tests.py`` in the application directory -- i.e., the |
|
directory that holds ``models.py``. This file is a hook for any and all |
|
doctests you want to write that aren't necessarily related to models. |
|
|
|
Here is an example model doctest:: |
|
|
|
# models.py |
|
|
|
from django.db import models |
|
|
|
class Animal(models.Model): |
|
""" |
|
An animal that knows how to make noise |
|
|
|
# Create some animals |
|
>>> lion = Animal.objects.create(name="lion", sound="roar") |
|
>>> cat = Animal.objects.create(name="cat", sound="meow") |
|
|
|
# Make 'em speak |
|
>>> lion.speak() |
|
'The lion says "roar"' |
|
>>> cat.speak() |
|
'The cat says "meow"' |
|
""" |
|
name = models.CharField(max_length=20) |
|
sound = models.CharField(max_length=20) |
|
|
|
def speak(self): |
|
return 'The %s says "%s"' % (self.name, self.sound) |
|
|
|
When you :ref:`run your tests <running-tests>`, the test runner will find this |
|
docstring, notice that portions of it look like an interactive Python session, |
|
and execute those lines while checking that the results match. |
|
|
|
In the case of model tests, note that the test runner takes care of creating |
|
its own test database. That is, any test that accesses a database -- by |
|
creating and saving model instances, for example -- will not affect your |
|
production database. Each doctest begins with a "blank slate" -- a fresh |
|
database containing an empty table for each model. (See the section on |
|
fixtures, below, for more on this.) Note that to use this feature, the database |
|
user Django is connecting as must have ``CREATE DATABASE`` rights. |
|
|
|
For more details about how doctest works, see the `standard library |
|
documentation for doctest`_. |
|
|
|
.. _doctest: http://docs.python.org/lib/module-doctest.html |
|
.. _standard library documentation for doctest: doctest_ |
|
|
|
Writing unit tests |
|
------------------ |
|
|
|
Like doctests, Django's unit tests use a standard library module: unittest_. |
|
This module uses a different way of defining tests, taking a class-based |
|
approach. |
|
|
|
As with doctests, for a given Django application, the test runner looks for |
|
unit tests in two places: |
|
|
|
* The ``models.py`` file. The test runner looks for any subclass of |
|
``unittest.TestCase`` in this module. |
|
|
|
* A file called ``tests.py`` in the application directory -- i.e., the |
|
directory that holds ``models.py``. Again, the test runner looks for any |
|
subclass of ``unittest.TestCase`` in this module. |
|
|
|
This example ``unittest.TestCase`` subclass is equivalent to the example given |
|
in the doctest section above:: |
|
|
|
import unittest |
|
from myapp.models import Animal |
|
|
|
class AnimalTestCase(unittest.TestCase): |
|
def setUp(self): |
|
self.lion = Animal.objects.create(name="lion", sound="roar") |
|
self.cat = Animal.objects.create(name="cat", sound="meow") |
|
|
|
def testSpeaking(self): |
|
self.assertEquals(self.lion.speak(), 'The lion says "roar"') |
|
self.assertEquals(self.cat.speak(), 'The cat says "meow"') |
|
|
|
When you :ref:`run your tests <running-tests>`, the default behavior of the |
|
test utility is to find all the test cases (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. |
|
|
|
In the Django development version, there is a second way to define the test |
|
suite for a module: if you define a function called ``suite()`` in either |
|
``models.py`` or ``tests.py``, the Django test runner will use that function |
|
to construct the test suite for that module. This follows the `suggested |
|
organization`_ for unit tests. See the Python documentation for more details on |
|
how to construct a complex test suite. |
|
|
|
For more details about ``unittest``, see the `standard library unittest |
|
documentation`_. |
|
|
|
.. _unittest: http://docs.python.org/lib/module-unittest.html |
|
.. _standard library unittest documentation: unittest_ |
|
.. _suggested organization: http://docs.python.org/lib/organizing-tests.html |
|
|
|
Which should I use? |
|
------------------- |
|
|
|
Because Django supports both of the standard Python test frameworks, it's up to |
|
you and your tastes to decide which one to use. You can even decide to use |
|
*both*. |
|
|
|
For developers new to testing, however, this choice can seem confusing. Here, |
|
then, are a few key differences to help you decide which approach is right for |
|
you: |
|
|
|
* If you've been using Python for a while, ``doctest`` will probably feel |
|
more "pythonic". It's designed to make writing tests as easy as possible, |
|
so it requires no overhead of writing classes or methods. You simply put |
|
tests in docstrings. This has the added advantage of serving as |
|
documentation (and correct documentation, at that!). |
|
|
|
If you're just getting started with testing, using doctests will probably |
|
get you started faster. |
|
|
|
* The ``unittest`` framework will probably feel very familiar to developers |
|
coming from Java. ``unittest`` is inspired by Java's JUnit, so you'll |
|
feel at home with this method if you've used JUnit or any test framework |
|
inspired by JUnit. |
|
|
|
* If you need to write a bunch of tests that share similar code, then |
|
you'll appreciate the ``unittest`` framework's organization around |
|
classes and methods. This makes it easy to abstract common tasks into |
|
common methods. The framework also supports explicit setup and/or cleanup |
|
routines, which give you a high level of control over the environment |
|
in which your test cases are run. |
|
|
|
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. |
|
|
|
.. _running-tests: |
|
|
|
Running tests |
|
============= |
|
|
|
Once you've written tests, run them using your project's ``manage.py`` |
|
utility:: |
|
|
|
$ ./manage.py test |
|
|
|
By default, this will run every test in every application in |
|
:setting:`INSTALLED_APPS`. If you only want to run tests for a particular |
|
application, add the application name to the command line. For example, if your |
|
:setting:`INSTALLED_APPS` contains ``'myproject.polls'`` and |
|
``'myproject.animals'``, you can run the ``myproject.animals`` unit tests alone |
|
with this command:: |
|
|
|
$ ./manage.py test animals |
|
|
|
Note that we used ``animals``, not ``myproject.animals``. |
|
|
|
.. versionadded:: 1.0 |
|
You can now choose which test to run. |
|
|
|
If you use unit tests, as opposed to |
|
doctests, you can be even *more* specific in choosing which tests to execute. |
|
To run a single test case in an application (for example, the |
|
``AnimalTestCase`` described in the "Writing unit tests" section), add the |
|
name of the test case to the label on the command line:: |
|
|
|
$ ./manage.py test animals.AnimalTestCase |
|
|
|
And it gets even more granular than that! To run a *single* test method inside |
|
a test case, add the name of the test method to the label:: |
|
|
|
$ ./manage.py test animals.AnimalTestCase.testFluffyAnimals |
|
|
|
The test database |
|
----------------- |
|
|
|
Tests that require a database (namely, model tests) will not use your "real" |
|
(production) database. A separate, blank database is created for the tests. |
|
|
|
Regardless of whether the tests pass or fail, the test database is destroyed |
|
when all the tests have been executed. |
|
|
|
By default this test database gets its name by prepending ``test_`` to the |
|
value of the :setting:`DATABASE_NAME` setting. When using the SQLite database |
|
engine the tests will by default use an in-memory database (i.e., the database |
|
will be created in memory, bypassing the filesystem entirely!). If you want to |
|
use a different database name, specify the :setting:`TEST_DATABASE_NAME` |
|
setting. |
|
|
|
Aside from using a separate database, the test runner will otherwise use all of |
|
the same database settings you have in your settings file: |
|
:setting:`DATABASE_ENGINE`, :setting:`DATABASE_USER`, :setting:`DATABASE_HOST`, |
|
etc. The test database is created by the user specified by |
|
:setting:`DATABASE_USER`, so you'll need to make sure that the given user |
|
account has sufficient privileges to create a new database on the system. |
|
|
|
.. versionadded:: 1.0 |
|
|
|
For fine-grained control over the |
|
character encoding of your test database, use the |
|
:setting:`TEST_DATABASE_CHARSET` setting. If you're using MySQL, you can also |
|
use the :setting:`TEST_DATABASE_COLLATION` setting to control the particular |
|
collation used by the test database. See the :ref:`settings documentation |
|
<ref-settings>` for details of these advanced settings. |
|
|
|
Other test conditions |
|
--------------------- |
|
|
|
Regardless of the value of the :setting:`DEBUG` setting in your configuration |
|
file, all Django tests run with :setting:`DEBUG=False`. This is to ensure that |
|
the observed output of your code matches what will be seen in a production |
|
setting. |
|
|
|
Understanding the test output |
|
----------------------------- |
|
|
|
When you run your tests, you'll see a number of messages as the test runner |
|
prepares itself. You can control the level of detail of these messages with the |
|
``verbosity`` option on the command line:: |
|
|
|
Creating test database... |
|
Creating table myapp_animal |
|
Creating table myapp_mineral |
|
Loading 'initial_data' fixtures... |
|
No fixtures found. |
|
|
|
This tells you that the test runner is creating a test database, as described |
|
in the previous section. |
|
|
|
Once the test database has been created, Django will run your tests. |
|
If everything goes well, you'll see something like this:: |
|
|
|
---------------------------------------------------------------------- |
|
Ran 22 tests in 0.221s |
|
|
|
OK |
|
|
|
If there are test failures, however, you'll see full details about which tests |
|
failed:: |
|
|
|
====================================================================== |
|
FAIL: Doctest: ellington.core.throttle.models |
|
---------------------------------------------------------------------- |
|
Traceback (most recent call last): |
|
File "/dev/django/test/doctest.py", line 2153, in runTest |
|
raise self.failureException(self.format_failure(new.getvalue())) |
|
AssertionError: Failed doctest test for myapp.models |
|
File "/dev/myapp/models.py", line 0, in models |
|
|
|
---------------------------------------------------------------------- |
|
File "/dev/myapp/models.py", line 14, in myapp.models |
|
Failed example: |
|
throttle.check("actor A", "action one", limit=2, hours=1) |
|
Expected: |
|
True |
|
Got: |
|
False |
|
|
|
---------------------------------------------------------------------- |
|
Ran 2 tests in 0.048s |
|
|
|
FAILED (failures=1) |
|
|
|
A full explanation of this error output is beyond the scope of this document, |
|
but it's pretty intuitive. You can consult the documentation of Python's |
|
``unittest`` library for details. |
|
|
|
Note that the return code for the test-runner script is the total number of |
|
failed and erroneous tests. If all the tests pass, the return code is 0. This |
|
feature is useful if you're using the test-runner script in a shell script and |
|
need to test for success or failure at that level. |
|
|
|
Testing tools |
|
============= |
|
|
|
Django provides a small set of tools that come in handy when writing tests. |
|
|
|
The test client |
|
--------------- |
|
|
|
.. module:: django.test.client |
|
:synopsis: Django's test client. |
|
|
|
The test client is a Python class that acts as a dummy Web browser, allowing |
|
you to test your views and interact with your Django-powered application |
|
programmatically. |
|
|
|
Some of the things you can do with the test client are: |
|
|
|
* Simulate GET and POST requests on a URL and observe the response -- |
|
everything from low-level HTTP (result headers and status codes) to |
|
page content. |
|
|
|
* Test that the correct view is executed for a given URL. |
|
|
|
* Test that a given request is rendered by a given Django template, with |
|
a template context that contains certain values. |
|
|
|
Note that the test client is not intended to be a replacement for Twill_, |
|
Selenium_, or other "in-browser" frameworks. Django's test client has |
|
a different focus. In short: |
|
|
|
* Use Django's test client to establish that the correct view is being |
|
called and that the view is collecting the correct context data. |
|
|
|
* Use in-browser frameworks such as Twill and Selenium to test *rendered* |
|
HTML and the *behavior* of Web pages, namely JavaScript functionality. |
|
|
|
A comprehensive test suite should use a combination of both test types. |
|
|
|
.. _Twill: http://twill.idyll.org/ |
|
.. _Selenium: http://www.openqa.org/selenium/ |
|
|
|
Overview and a quick example |
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
To use the test client, instantiate ``django.test.client.Client`` and retrieve |
|
Web pages:: |
|
|
|
>>> from django.test.client import Client |
|
>>> c = Client() |
|
>>> response = c.post('/login/', {'username': 'john', 'password': 'smith'}) |
|
>>> response.status_code |
|
200 |
|
>>> response = c.get('/customer/details/') |
|
>>> response.content |
|
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 ...' |
|
|
|
As this example suggests, you can instantiate ``Client`` from within a session |
|
of the Python interactive interpreter. |
|
|
|
Note a few important things about how the test client works: |
|
|
|
* The test client does *not* require the Web server to be running. In fact, |
|
it will run just fine with no Web server running at all! That's because |
|
it avoids the overhead of HTTP and deals directly with the Django |
|
framework. This helps make the unit tests run quickly. |
|
|
|
* When retrieving pages, remember to specify the *path* of the URL, not the |
|
whole domain. For example, this is correct:: |
|
|
|
>>> c.get('/login/') |
|
|
|
This is incorrect:: |
|
|
|
>>> c.get('http://www.example.com/login/') |
|
|
|
The test client is not capable of retrieving Web pages that are not |
|
powered by your Django project. If you need to retrieve other Web pages, |
|
use a Python standard library module such as urllib_ or urllib2_. |
|
|
|
* To resolve URLs, the test client uses whatever URLconf is pointed-to by |
|
your :setting:`ROOT_URLCONF` setting. |
|
|
|
* Although the above example would work in the Python interactive |
|
interpreter, some of the test client's functionality, notably the |
|
template-related functionality, is only available *while tests are |
|
running*. |
|
|
|
The reason for this is that Django's test runner performs a bit of black |
|
magic in order to determine which template was loaded by a given view. |
|
This black magic (essentially a patching of Django's template system in |
|
memory) only happens during test running. |
|
|
|
.. _urllib: http://docs.python.org/lib/module-urllib.html |
|
.. _urllib2: http://docs.python.org/lib/module-urllib2.html |
|
|
|
Making requests |
|
~~~~~~~~~~~~~~~ |
|
|
|
Use the ``django.test.client.Client`` class to make requests. It requires no |
|
arguments at time of construction: |
|
|
|
.. class:: Client() |
|
|
|
Once you have a ``Client`` instance, you can call any of the following |
|
methods: |
|
|
|
.. method:: Client.get(path, data={}) |
|
|
|
Makes a GET request on the provided ``path`` and returns a ``Response`` |
|
object, which is documented below. |
|
|
|
The key-value pairs in the ``data`` dictionary are 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:: |
|
|
|
/customers/details/?name=fred&age=7 |
|
|
|
.. method:: Client.post(path, data={}, content_type=MULTIPART_CONTENT) |
|
|
|
Makes a POST request on the provided ``path`` and returns a |
|
``Response`` object, which is documented below. |
|
|
|
The key-value pairs in the ``data`` dictionary are used to submit POST |
|
data. For example:: |
|
|
|
>>> c = Client() |
|
>>> c.post('/login/', {'name': 'fred', 'passwd': 'secret'}) |
|
|
|
...will result in the evaluation of a POST request to this URL:: |
|
|
|
/login/ |
|
|
|
...with this POST data:: |
|
|
|
name=fred&passwd=secret |
|
|
|
If you provide ``content_type`` (e.g., ``text/xml`` for an XML |
|
payload), the contents of ``data`` will be sent as-is in the POST |
|
request, using ``content_type`` in the HTTP ``Content-Type`` header. |
|
|
|
If you don't provide a value for ``content_type``, the values in |
|
``data`` will be transmitted with a content type of |
|
``multipart/form-data``. In this case, the key-value pairs in ``data`` |
|
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 ``<select multiple>`` -- provide the values as a |
|
list or tuple for the required key. For example, this value of ``data`` |
|
would submit three selected values for the field named ``choices``:: |
|
|
|
{'choices': ('a', 'b', 'd')} |
|
|
|
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. For example:: |
|
|
|
>>> c = Client() |
|
>>> f = open('wishlist.doc') |
|
>>> c.post('/customers/wishes/', {'name': 'fred', 'attachment': f}) |
|
>>> f.close() |
|
|
|
(The name ``attachment`` here is not relevant; use whatever name your |
|
file-processing code expects.) |
|
|
|
Note that you should manually close the file after it has been provided |
|
to ``post()``. |
|
|
|
.. method:: Client.login(**credentials) |
|
|
|
.. versionadded:: 1.0 |
|
|
|
If your site uses Django's :ref:`authentication system<topics-auth>` |
|
and you deal with logging in users, you can use the test client's |
|
``login()`` method to simulate the effect of a user logging into the |
|
site. |
|
|
|
After you call this method, the test client will have all the cookies |
|
and session data required to pass any login-based tests that may form |
|
part of a view. |
|
|
|
The format of the ``credentials`` argument depends on which |
|
:ref:`authentication backend <authentication-backends>` you're using |
|
(which is configured by your :setting:`AUTHENTICATION_BACKENDS` |
|
setting). If you're using the standard authentication backend provided |
|
by Django (``ModelBackend``), ``credentials`` should be the user's |
|
username and password, provided as keyword arguments:: |
|
|
|
>>> c = Client() |
|
>>> c.login(username='fred', password='secret') |
|
|
|
# Now you can access a view that's only available to logged-in users. |
|
|
|
If you're using a different authentication backend, this method may |
|
require different credentials. It requires whichever credentials are |
|
required by your backend's ``authenticate()`` method. |
|
|
|
``login()`` returns ``True`` if it the credentials were accepted and |
|
login was successful. |
|
|
|
Finally, you'll need to remember to create user accounts before you can |
|
use this method. As we explained above, the test runner is executed |
|
using a test database, which contains no users by default. As a result, |
|
user accounts that are valid on your production site will not work |
|
under test conditions. You'll need to create users as part of the test |
|
suite -- either manually (using the Django model API) or with a test |
|
fixture. |
|
|
|
.. method:: Client.logout() |
|
|
|
.. versionadded:: 1.0 |
|
|
|
If your site uses Django's :ref:`authentication system<topics-auth>`, |
|
the ``logout()`` method can be used to simulate the effect of a user |
|
logging out of your site. |
|
|
|
After you call this method, the test client will have all the cookies |
|
and session data cleared to defaults. Subsequent requests will appear |
|
to come from an AnonymousUser. |
|
|
|
Testing responses |
|
~~~~~~~~~~~~~~~~~ |
|
|
|
The ``get()`` and ``post()`` methods both return a ``Response`` object. This |
|
``Response`` object is *not* the same as the ``HttpResponse`` object returned |
|
Django views; the test response object has some additional data useful for |
|
test code to verify. |
|
|
|
Specifically, a ``Response`` object has the following attributes: |
|
|
|
.. class:: Response() |
|
|
|
.. attribute:: client |
|
|
|
The test client that was used to make the request that resulted in the |
|
response. |
|
|
|
.. attribute:: content |
|
|
|
The body of the response, as a string. This is the final page content as |
|
rendered by the view, or any error message. |
|
|
|
.. attribute:: context |
|
|
|
The template ``Context`` instance that was used to render the template that |
|
produced the response content. |
|
|
|
If the rendered page used multiple templates, then ``context`` will be a |
|
list of ``Context`` objects, in the order in which they were rendered. |
|
|
|
.. attribute:: request |
|
|
|
The request data that stimulated the response. |
|
|
|
.. attribute:: status_code |
|
|
|
The HTTP status of the response, as an integer. See RFC2616_ for a full |
|
list of HTTP status codes. |
|
|
|
.. attribute:: template |
|
|
|
The ``Template`` instance that was used to render the final content. Use |
|
``template.name`` to get the template's file name, if the template was |
|
loaded from a file. (The name is a string such as ``'admin/index.html'``.) |
|
|
|
If the rendered page used multiple templates -- e.g., using :ref:`template |
|
inheritance<template-inheritance>` -- then ``template`` will be a list of |
|
``Template`` instances, in the order in which they were rendered. |
|
|
|
You can also use dictionary syntax on the response object to query the value |
|
of any settings in the HTTP headers. For example, you could determine the |
|
content type of a response using ``response['Content-Type']``. |
|
|
|
.. _RFC2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html |
|
|
|
Exceptions |
|
~~~~~~~~~~ |
|
|
|
If you point the test client at a view that raises an exception, that exception |
|
will be visible in the test case. You can then use a standard ``try...catch`` |
|
block or ``unittest.TestCase.assertRaises()`` to test for exceptions. |
|
|
|
The only exceptions that are not visible to the test client are ``Http404``, |
|
``PermissionDenied`` and ``SystemExit``. Django catches these exceptions |
|
internally and converts them into the appropriate HTTP response codes. In these |
|
cases, you can check ``response.status_code`` in your test. |
|
|
|
Persistent state |
|
~~~~~~~~~~~~~~~~ |
|
|
|
The test client is stateful. If a response returns a cookie, then that cookie |
|
will be stored in the test client and sent with all subsequent ``get()`` and |
|
``post()`` requests. |
|
|
|
Expiration policies for these cookies are not followed. If you want a cookie |
|
to expire, either delete it manually or create a new ``Client`` instance (which |
|
will effectively delete all cookies). |
|
|
|
A test client has two attributes that store persistent state information. You |
|
can access these properties as part of a test condition. |
|
|
|
.. attribute:: Client.cookies |
|
|
|
A Python ``SimpleCookie`` object, containing the current values of all the |
|
client cookies. See the `Cookie module documentation`_ for more. |
|
|
|
.. attribute:: Client.session |
|
|
|
A dictionary-like object containing session information. See the |
|
:ref:`session documentation<topics-http-sessions>` for full details. |
|
|
|
.. _Cookie module documentation: http://docs.python.org/lib/module-Cookie.html |
|
|
|
Example |
|
~~~~~~~ |
|
|
|
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 response is 200 OK. |
|
self.failUnlessEqual(response.status_code, 200) |
|
|
|
# Check that the rendered context contains 5 customers. |
|
self.failUnlessEqual(len(response.context['customers']), 5) |
|
|
|
TestCase |
|
-------- |
|
|
|
.. currentmodule:: django.test |
|
|
|
Normal Python unit test classes extend a base class of ``unittest.TestCase``. |
|
Django provides an extension of this base class: |
|
|
|
.. class:: TestCase() |
|
|
|
This class provides some additional capabilities that can be useful for testing |
|
Web sites. |
|
|
|
Converting 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 functionality |
|
will continue to be available, but it will be augmented with some useful |
|
additions. |
|
|
|
Default test client |
|
~~~~~~~~~~~~~~~~~~~ |
|
|
|
.. versionadded:: 1.0 |
|
|
|
.. attribute:: TestCase.client |
|
|
|
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, so you don't have to |
|
worry about state (such as cookies) carrying over from one test to another. |
|
|
|
This means, instead of instantiating a ``Client`` in each test:: |
|
|
|
import unittest |
|
from django.test.client import Client |
|
|
|
class SimpleTest(unittest.TestCase): |
|
def test_details(self): |
|
client = Client() |
|
response = client.get('/customer/details/') |
|
self.failUnlessEqual(response.status_code, 200) |
|
|
|
def test_index(self): |
|
client = Client() |
|
response = client.get('/customer/index/') |
|
self.failUnlessEqual(response.status_code, 200) |
|
|
|
...you can just refer to ``self.client``, like so:: |
|
|
|
from django.test import TestCase |
|
|
|
class SimpleTest(TestCase): |
|
def test_details(self): |
|
response = self.client.get('/customer/details/') |
|
self.failUnlessEqual(response.status_code, 200) |
|
|
|
def test_index(self): |
|
response = self.client.get('/customer/index/') |
|
self.failUnlessEqual(response.status_code, 200) |
|
|
|
.. _topics-testing-fixtures: |
|
|
|
Fixture loading |
|
~~~~~~~~~~~~~~~ |
|
|
|
.. attribute:: TestCase.fixtures |
|
|
|
A test case for a database-backed Web site 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's custom ``TestCase`` class provides a way of loading **fixtures**. |
|
|
|
A fixture is a collection of data that Django knows how to import into a |
|
database. For example, if your site has user accounts, you might set up a |
|
fixture of fake user accounts in order to populate your database during tests. |
|
|
|
The most straightforward way of creating a fixture is to use the ``manage.py |
|
dumpdata`` command. This assumes you already have some data in your database. |
|
See the :djadmin:`dumpdata documentation<dumpdata>` for more details. |
|
|
|
.. note:: |
|
If you've ever run ``manage.py syncdb``, you've already used a fixture |
|
without even knowing it! When you call ``syncdb`` in the database for |
|
the first time, Django installs a fixture called ``initial_data``. |
|
This gives you a way of populating a new database with any initial data, |
|
such as a default set of categories. |
|
|
|
Fixtures with other names can always be installed manually using the |
|
``manage.py loaddata`` command. |
|
|
|
Once you've created a fixture and placed it somewhere in your Django project, |
|
you can use it in your unit tests by specifying a ``fixtures`` class attribute |
|
on your ``django.test.TestCase`` subclass:: |
|
|
|
from django.test import TestCase |
|
from myapp.models import Animal |
|
|
|
class AnimalTestCase(TestCase): |
|
fixtures = ['mammals.json', 'birds'] |
|
|
|
def setUp(self): |
|
# Test definitions as before. |
|
|
|
def testFluffyAnimals(self): |
|
# A test that uses the fixtures. |
|
|
|
Here's specifically what will happen: |
|
|
|
* At the start of each test case, before ``setUp()`` is run, Django will |
|
flush the database, returning the database to the state it was in |
|
directly after ``syncdb`` was called. |
|
|
|
* Then, all the named fixtures are installed. In this example, Django will |
|
install any JSON fixture named ``mammals``, followed by any fixture named |
|
``birds``. See the :djadmin:`loaddata documentation<loaddata>` for more |
|
details on defining and installing fixtures. |
|
|
|
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 by the order of test execution. |
|
|
|
URLconf configuration |
|
~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
.. versionadded:: 1.0 |
|
|
|
.. attribute:: TestCase.urls |
|
|
|
If your application provides views, you may want to include tests that use the |
|
test client to exercise those views. However, an end user is free to deploy the |
|
views in your application at any URL of their choosing. This means that your |
|
tests can't rely upon the fact that your views will be available at a |
|
particular URL. |
|
|
|
In order to provide a reliable URL space for your test, |
|
``django.test.TestCase`` provides the ability to customize the URLconf |
|
configuration for the duration of the execution of a test suite. If your |
|
``TestCase`` instance defines an ``urls`` attribute, the ``TestCase`` will use |
|
the value of that attribute as the ``ROOT_URLCONF`` for the duration of that |
|
test. |
|
|
|
For example:: |
|
|
|
from django.test import TestCase |
|
|
|
class TestMyViews(TestCase): |
|
urls = 'myapp.test_urls' |
|
|
|
def testIndexPageView(self): |
|
# Here you'd test your view using ``Client``. |
|
|
|
This test case will use the contents of ``myapp.test_urls`` as the |
|
URLconf for the duration of the test case. |
|
|
|
.. _emptying-test-outbox: |
|
|
|
Emptying the test outbox |
|
~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
.. versionadded:: 1.0 |
|
|
|
If you use Django's custom ``TestCase`` class, the test runner will clear the |
|
contents of the test e-mail outbox at the start of each test case. |
|
|
|
For more detail on e-mail services during tests, see `E-mail services`_. |
|
|
|
Assertions |
|
~~~~~~~~~~ |
|
|
|
.. versionadded:: 1.0 |
|
|
|
As Python's normal ``unittest.TestCase`` class implements assertion methods |
|
such as ``assertTrue`` and ``assertEquals``, Django's custom ``TestCase`` class |
|
provides a number of custom assertion methods that are useful for testing Web |
|
applications: |
|
|
|
.. method:: TestCase.assertContains(response, text, count=None, status_code=200) |
|
|
|
Asserts that a ``Response`` instance produced the given ``status_code`` and |
|
that ``text`` appears in the content of the response. If ``count`` is |
|
provided, ``text`` must occur exactly ``count`` times in the response. |
|
|
|
.. method:: TestCase.assertNotContains(response, text, status_code=200) |
|
|
|
Asserts that a ``Response`` instance produced the given ``status_code`` and |
|
that ``text`` does not appears in the content of the response. |
|
|
|
.. method:: assertFormError(response, form, field, errors) |
|
|
|
Asserts that a field on a form raises the provided list of errors when |
|
rendered on the form. |
|
|
|
``form`` is the name the ``Form`` instance 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 (errors you can access via |
|
``form.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. |
|
|
|
.. method:: assertTemplateUsed(response, template_name) |
|
|
|
Asserts that the template with the given name was used in rendering the |
|
response. |
|
|
|
The name is a string such as ``'admin/index.html'``. |
|
|
|
.. method:: assertTemplateNotUsed(response, template_name) |
|
|
|
Asserts that the template with the given name was *not* used in rendering |
|
the response. |
|
|
|
.. method:: assertRedirects(response, expected_url, status_code=302, target_status_code=200) |
|
|
|
Asserts that the response return a ``status_code`` redirect status, it |
|
redirected to ``expected_url`` (including any GET data), and the subsequent |
|
page was received with ``target_status_code``. |
|
|
|
E-mail services |
|
--------------- |
|
|
|
.. versionadded:: 1.0 |
|
|
|
If any of your Django views send e-mail using :ref:`Django's e-mail |
|
functionality <topics-email>`, you probably don't want to send e-mail each time |
|
you run a test using that view. For this reason, Django's test runner |
|
automatically redirects all Django-sent e-mail to a dummy outbox. This lets you |
|
test every aspect of sending e-mail -- from the number of messages sent to the |
|
contents of each message -- without actually sending the messages. |
|
|
|
The test runner accomplishes this by transparently replacing the normal |
|
:class:`~django.core.mail.SMTPConnection` class with a different version. |
|
(Don't worry -- this has no effect on any other e-mail senders outside of |
|
Django, such as your machine's mail server, if you're running one.) |
|
|
|
.. currentmodule:: django.core.mail |
|
|
|
.. data:: django.core.mail.output |
|
|
|
During test running, each outgoing e-mail is saved in |
|
``django.core.mail.outbox``. This is a simple list of all |
|
:class:`<~django.core.mail.EmailMessage>` instances that have been sent. |
|
It does not exist under normal execution conditions, i.e., when you're not |
|
running unit tests. The outbox is created during test setup, along with the |
|
dummy :class:`<~django.core.mail.SMTPConnection>`. When the test framework is |
|
torn down, the standard :class:`<~django.core.mail.SMTPConnection>` class is |
|
restored, and the test outbox is destroyed. |
|
|
|
Here's an example test that examines ``django.core.mail.outbox`` for length |
|
and contents:: |
|
|
|
from django.core import mail |
|
from django.test import TestCase |
|
|
|
class EmailTest(TestCase): |
|
def test_send_email(self): |
|
# Send message. |
|
mail.send_mail('Subject here', 'Here is the message.', |
|
'from@example.com', ['to@example.com'], |
|
fail_silently=False) |
|
|
|
# Test that one message has been sent. |
|
self.assertEqual(len(mail.outbox), 1) |
|
|
|
# Verify that the subject of the first message is correct. |
|
self.assertEqual(mail.outbox[0].subject, 'Subject here') |
|
|
|
As noted :ref:`previously <emptying-test-outbox>`, the test outbox is emptied |
|
at the start of every test in a Django ``TestCase``. To empty the outbox |
|
manually, assign the empty list to ``mail.outbox``:: |
|
|
|
from django.core import mail |
|
|
|
# Empty the test outbox |
|
mail.outbox = [] |
|
|
|
Using different testing frameworks |
|
================================== |
|
|
|
Clearly, ``doctest`` and ``unittest`` are not the only Python testing |
|
frameworks. While Django doesn't provide explicit support for alternative |
|
frameworks, it does provide a way 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 :setting:`TEST_RUNNER` |
|
setting to determine what to do. By default, :setting:`TEST_RUNNER` points to |
|
``'django.test.simple.run_tests'``. This method defines the default Django |
|
testing behavior. This behavior 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 the ``models.py`` and |
|
``tests.py`` files in 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 :setting:`TEST_RUNNER` at |
|
that method, Django will execute your test runner whenever you run |
|
``./manage.py test``. In this way, it is possible to use any test framework |
|
that can be executed from Python code. |
|
|
|
Defining a test runner |
|
---------------------- |
|
|
|
.. versionadded:: 1.0 |
|
|
|
.. currentmodule:: django.test.simple |
|
|
|
By convention, a test runner should be called ``run_tests``. The only strict |
|
requirement is that it has the same arguments as the Django test runner: |
|
|
|
.. function:: run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[]) |
|
|
|
``test_labels`` is a list of strings describing the tests to be run. A test |
|
label can take one of three forms: |
|
|
|
* ``app.TestCase.test_method`` -- Run a single test method in a test |
|
case. |
|
* ``app.TestCase`` -- Run all the test methods in a test case. |
|
* ``app`` -- Search for and run all tests in the named application. |
|
|
|
If ``test_labels`` has a value of ``None``, the test runner should run |
|
search for tests in all the applications in :setting:`INSTALLED_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. |
|
|
|
If ``interactive`` is ``True``, the test suite has permission to ask the |
|
user for instructions when the test suite is executed. An example of this |
|
behavior would be asking for permission to delete an existing test |
|
database. If ``interactive`` is ``False``, the test suite must be able to |
|
run without any manual intervention. |
|
|
|
``extra_tests`` is a list of extra ``TestCase`` instances to add to the |
|
suite that is executed by the test runner. These extra tests are run |
|
in addition to those discovered in the modules listed in ``module_list``. |
|
|
|
This method should return the number of tests that failed. |
|
|
|
Testing utilities |
|
----------------- |
|
|
|
.. module:: django.test.utils |
|
:synopsis: Helpers to write custom test runners. |
|
|
|
To assist in the creation of your own test runner, Django provides a number of |
|
utility methods in the ``django.test.utils`` module. |
|
|
|
.. function:: setup_test_environment() |
|
|
|
Performs any global pre-test setup, such as the installing the |
|
instrumentation of the template rendering system and setting up the dummy |
|
``SMTPConnection``. |
|
|
|
.. function:: teardown_test_environment() |
|
|
|
Performs any global post-test teardown, such as removing the black magic |
|
hooks into the template system and restoring normal e-mail services. |
|
|
|
The creation module of the database backend (``connection.creation``) also |
|
provides some utilities that can be useful during testing. |
|
|
|
.. function:: create_test_db(verbosity=1, autoclobber=False) |
|
|
|
Creates a new test database and runs ``syncdb`` against it. |
|
|
|
``verbosity`` has the same behavior as in ``run_tests()``. |
|
|
|
``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. |
|
|
|
Returns the name of the test database that it created. |
|
|
|
``create_test_db()`` has the side effect of modifying |
|
``settings.DATABASE_NAME`` to match the name of the test database. |
|
|
|
.. versionchanged:: 1.0 |
|
``create_test_db()`` now returns the name of the test database. |
|
|
|
.. function:: destroy_test_db(old_database_name, verbosity=1) |
|
|
|
Destroys the database whose name is in the :setting:`DATABASE_NAME` setting |
|
and restores the value of :setting:`DATABASE_NAME` to the provided name. |
|
|
|
``verbosity`` has the same behavior as in ``run_tests()``.
|
|
|