| 145 | | Choosing a test framework is often contentious, so Django simply supports |
|---|
| 146 | | both of the standard Python test frameworks. Choosing one is up to each |
|---|
| 147 | | developer's personal tastes; each is supported equally. Since each test |
|---|
| 148 | | system has different benefits, the best approach is probably to use both |
|---|
| 149 | | together, picking the test system to match the type of tests you need to |
|---|
| 150 | | write. |
|---|
| 151 | | |
|---|
| 152 | | For developers new to testing, however, this choice can seem |
|---|
| 153 | | confusing, so here are a few key differences to help you decide whether |
|---|
| 154 | | doctests or unit tests are right for you. |
|---|
| 155 | | |
|---|
| 156 | | If you've been using Python for a while, ``doctest`` will probably feel more |
|---|
| 157 | | "pythonic". It's designed to make writing tests as easy as possible, so |
|---|
| 158 | | there's no overhead of writing classes or methods; you simply put tests in |
|---|
| 159 | | docstrings. This gives the added advantage of giving your modules automatic |
|---|
| 160 | | documentation -- well-written doctests can kill both the documentation and the |
|---|
| 161 | | testing bird with a single stone. |
|---|
| 162 | | |
|---|
| 163 | | For developers just getting started with testing, using doctests will probably |
|---|
| 164 | | get you started faster. |
|---|
| 165 | | |
|---|
| 166 | | The ``unittest`` framework will probably feel very familiar to developers |
|---|
| 167 | | coming from Java. Since ``unittest`` is inspired by Java's JUnit, if |
|---|
| 168 | | you've used testing frameworks in other languages that similarly were |
|---|
| 169 | | inspired by JUnit, ``unittest`` should also feel pretty familiar. |
|---|
| 170 | | |
|---|
| 171 | | Since ``unittest`` is organized around classes and methods, if you need |
|---|
| 172 | | to write a bunch of tests that all share similar code, you can easily use |
|---|
| 173 | | subclass to abstract common tasks; this makes test code shorter and cleaner. |
|---|
| 174 | | There's also support for explicit setup and/or cleanup routines, which give |
|---|
| 175 | | you a high level of control over the environment your test cases run in. |
|---|
| | 208 | Because Django supports both of the standard Python test frameworks, it's up to |
|---|
| | 209 | you and your tastes to decide which one to use. You can even decide to use |
|---|
| | 210 | *both*. |
|---|
| | 211 | |
|---|
| | 212 | For developers new to testing, however, this choice can seem confusing. Here, |
|---|
| | 213 | then, are a few key differences to help you decide which approach is right for |
|---|
| | 214 | you: |
|---|
| | 215 | |
|---|
| | 216 | * If you've been using Python for a while, ``doctest`` will probably feel |
|---|
| | 217 | more "pythonic". It's designed to make writing tests as easy as possible, |
|---|
| | 218 | so it requires no overhead of writing classes or methods. You simply put |
|---|
| | 219 | tests in docstrings. This has the added advantage of serving as |
|---|
| | 220 | documentation (and correct documentation, at that!). |
|---|
| | 221 | |
|---|
| | 222 | If you're just getting started with testing, using doctests will probably |
|---|
| | 223 | get you started faster. |
|---|
| | 224 | |
|---|
| | 225 | * The ``unittest`` framework will probably feel very familiar to developers |
|---|
| | 226 | coming from Java. ``unittest`` is inspired by Java's JUnit, so you'll |
|---|
| | 227 | feel at home with this method if you've used JUnit or any test framework |
|---|
| | 228 | inspired by JUnit. |
|---|
| | 229 | |
|---|
| | 230 | * If you need to write a bunch of tests that share similar code, then |
|---|
| | 231 | you'll appreciate the ``unittest`` framework's organization around |
|---|
| | 232 | classes and methods. This makes it easy to abstract common tasks into |
|---|
| | 233 | common methods. The framework also supports explicit setup and/or cleanup |
|---|
| | 234 | routines, which give you a high level of control over the environment |
|---|
| | 235 | in which your test cases are run. |
|---|
| 180 | | |
|---|
| 181 | | Testing Tools |
|---|
| 182 | | ============= |
|---|
| 183 | | |
|---|
| 184 | | To assist in testing various features of your application, Django provides |
|---|
| 185 | | tools that can be used to establish tests and test conditions. |
|---|
| 186 | | |
|---|
| 187 | | * `Test Client`_ |
|---|
| 188 | | * `TestCase`_ |
|---|
| 189 | | * `E-mail services`_ |
|---|
| 190 | | |
|---|
| 191 | | Test Client |
|---|
| 192 | | ----------- |
|---|
| 193 | | |
|---|
| 194 | | The Test Client is a simple dummy browser. It allows you to simulate |
|---|
| 195 | | GET and POST requests on a URL, and observe the response that is received. |
|---|
| 196 | | This allows you to test that the correct view is executed for a given URL, |
|---|
| 197 | | and that the view constructs the correct response. |
|---|
| 198 | | |
|---|
| 199 | | As the response is generated, the Test Client gathers details on the |
|---|
| 200 | | Template and Context objects that were used to generate the response. These |
|---|
| 201 | | Templates and Contexts are then provided as part of the response, and can be |
|---|
| 202 | | used as test conditions. |
|---|
| 203 | | |
|---|
| 204 | | .. admonition:: Test Client vs Browser Automation? |
|---|
| 205 | | |
|---|
| 206 | | The Test Client is not intended as a replacement for Twill_, Selenium_, |
|---|
| 207 | | or other browser automation frameworks - it is intended to allow |
|---|
| 208 | | testing of the contexts and templates produced by a view, |
|---|
| 209 | | rather than the HTML rendered to the end-user. |
|---|
| 210 | | |
|---|
| 211 | | A comprehensive test suite should use a combination of both: Test Client |
|---|
| 212 | | tests to establish that the correct view is being called and that |
|---|
| 213 | | the view is collecting the correct context data, and Browser Automation |
|---|
| 214 | | tests to check that user interface behaves as expected. |
|---|
| 215 | | |
|---|
| 216 | | .. _Twill: http://twill.idyll.org/ |
|---|
| 217 | | .. _Selenium: http://www.openqa.org/selenium/ |
|---|
| 218 | | |
|---|
| 219 | | Making requests |
|---|
| 220 | | ~~~~~~~~~~~~~~~ |
|---|
| 221 | | |
|---|
| 222 | | Creating an instance of ``Client`` (``django.test.client.Client``) requires |
|---|
| 223 | | no arguments at time of construction. Once constructed, the following methods |
|---|
| 224 | | can be invoked on the ``Client`` instance. |
|---|
| 225 | | |
|---|
| 226 | | ``get(path, data={})`` |
|---|
| 227 | | Make a GET request on the provided ``path``. The key-value pairs in the |
|---|
| 228 | | data dictionary will be used to create a GET data payload. For example:: |
|---|
| 229 | | |
|---|
| 230 | | c = Client() |
|---|
| 231 | | c.get('/customers/details/', {'name':'fred', 'age':7}) |
|---|
| 232 | | |
|---|
| 233 | | will result in the evaluation of a GET request equivalent to:: |
|---|
| 234 | | |
|---|
| 235 | | http://yoursite.com/customers/details/?name=fred&age=7 |
|---|
| 236 | | |
|---|
| 237 | | ``post(path, data={}, content_type=MULTIPART_CONTENT)`` |
|---|
| 238 | | Make a POST request on the provided ``path``. If you provide a content type |
|---|
| 239 | | (e.g., ``text/xml`` for an XML payload), the contents of ``data`` will be |
|---|
| 240 | | sent as-is in the POST request, using the content type in the HTTP |
|---|
| 241 | | ``Content-Type`` header. |
|---|
| 242 | | |
|---|
| 243 | | If you do not provide a value for ``content_type``, the values in |
|---|
| 244 | | ``data`` will be transmitted with a content type of ``multipart/form-data``. |
|---|
| 245 | | The key-value pairs in the data dictionary will be encoded as a multipart |
|---|
| 246 | | message and used to create the POST data payload. |
|---|
| 247 | | |
|---|
| 248 | | To submit multiple values for a given key (for example, to specify |
|---|
| 249 | | the selections for a multiple selection list), provide the values as a |
|---|
| 250 | | list or tuple for the required key. For example, a data dictionary of |
|---|
| 251 | | ``{'choices': ('a','b','d')}`` would submit three selected rows for the |
|---|
| 252 | | field named ``choices``. |
|---|
| 253 | | |
|---|
| 254 | | Submitting files is a special case. To POST a file, you need only |
|---|
| 255 | | provide the file field name as a key, and a file handle to the file you wish to |
|---|
| 256 | | upload as a value. The Test Client will populate the two POST fields (i.e., |
|---|
| 257 | | ``field`` and ``field_file``) required by Django's FileField. For example:: |
|---|
| 258 | | |
|---|
| 259 | | c = Client() |
|---|
| 260 | | f = open('wishlist.doc') |
|---|
| 261 | | c.post('/customers/wishes/', {'name':'fred', 'attachment':f}) |
|---|
| 262 | | f.close() |
|---|
| 263 | | |
|---|
| 264 | | will result in the evaluation of a POST request on ``/customers/wishes/``, |
|---|
| 265 | | with a POST dictionary that contains ``name``, ``attachment`` (containing the |
|---|
| 266 | | file name), and ``attachment_file`` (containing the file data). Note that you |
|---|
| 267 | | need to manually close the file after it has been provided to the POST. |
|---|
| 268 | | |
|---|
| 269 | | ``login(**credentials)`` |
|---|
| 270 | | **New in Django development version** |
|---|
| 271 | | |
|---|
| 272 | | On a production site, it is likely that some views will be protected from |
|---|
| 273 | | anonymous access through the use of the @login_required decorator, or some |
|---|
| 274 | | other login checking mechanism. The ``login()`` method can be used to |
|---|
| 275 | | simulate the effect of a user logging into the site. As a result of calling |
|---|
| 276 | | this method, the Client will have all the cookies and session data required |
|---|
| 277 | | to pass any login-based tests that may form part of a view. |
|---|
| 278 | | |
|---|
| 279 | | In most cases, the ``credentials`` required by this method are the username |
|---|
| 280 | | and password of the user that wants to log in, provided as keyword |
|---|
| 281 | | arguments:: |
|---|
| 282 | | |
|---|
| 283 | | c = Client() |
|---|
| 284 | | c.login(username='fred', password='secret') |
|---|
| 285 | | # Now you can access a login protected view |
|---|
| 286 | | |
|---|
| 287 | | If you are using a different authentication backend, this method may |
|---|
| 288 | | require different credentials. |
|---|
| 289 | | |
|---|
| 290 | | ``login()`` returns ``True`` if it the credentials were accepted and login |
|---|
| 291 | | was successful. |
|---|
| 292 | | |
|---|
| 293 | | Note that since the test suite will be executed using the test database, |
|---|
| 294 | | which contains no users by default. As a result, logins that are valid |
|---|
| 295 | | on your production site will not work under test conditions. You will |
|---|
| 296 | | need to create users as part of the test suite (either manually, or |
|---|
| 297 | | using a test fixture). |
|---|
| 298 | | |
|---|
| 299 | | Testing Responses |
|---|
| 300 | | ~~~~~~~~~~~~~~~~~ |
|---|
| 301 | | |
|---|
| 302 | | The ``get()`` and ``post()`` methods both return a Response object. This |
|---|
| 303 | | Response object has the following properties that can be used for testing |
|---|
| 304 | | purposes: |
|---|
| 305 | | |
|---|
| 306 | | =============== ========================================================== |
|---|
| 307 | | Property Description |
|---|
| 308 | | =============== ========================================================== |
|---|
| 309 | | ``status_code`` The HTTP status of the response. See RFC2616_ for a |
|---|
| 310 | | full list of HTTP status codes. |
|---|
| 311 | | |
|---|
| 312 | | ``content`` The body of the response. This is the final page |
|---|
| 313 | | content as rendered by the view, or any error message |
|---|
| 314 | | (such as the URL for a 302 redirect). |
|---|
| 315 | | |
|---|
| 316 | | ``template`` The Template instance that was used to render the final |
|---|
| 317 | | content. Testing ``template.name`` can be particularly |
|---|
| 318 | | useful; if the template was loaded from a file, |
|---|
| 319 | | ``template.name`` will be the file name that was loaded. |
|---|
| 320 | | |
|---|
| 321 | | If multiple templates were rendered, (e.g., if one |
|---|
| 322 | | template includes another template),``template`` will |
|---|
| 323 | | be a list of Template objects, in the order in which |
|---|
| 324 | | they were rendered. |
|---|
| 325 | | |
|---|
| 326 | | ``context`` The Context that was used to render the template that |
|---|
| 327 | | produced the response content. |
|---|
| 328 | | |
|---|
| 329 | | As with ``template``, if multiple templates were rendered |
|---|
| 330 | | ``context`` will be a list of Context objects, stored in |
|---|
| 331 | | the order in which they were rendered. |
|---|
| 332 | | =============== ========================================================== |
|---|
| 333 | | |
|---|
| 334 | | .. _RFC2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html |
|---|
| 335 | | |
|---|
| 336 | | Exceptions |
|---|
| 337 | | ~~~~~~~~~~ |
|---|
| 338 | | |
|---|
| 339 | | If you point the Test Client at a view that raises an exception, that exception |
|---|
| 340 | | will be visible in the test case. You can then use a standard ``try...catch`` |
|---|
| 341 | | block, or ``unittest.TestCase.assertRaises()`` to test for exceptions. |
|---|
| 342 | | |
|---|
| 343 | | The only exceptions that are not visible in a Test Case are ``Http404``, |
|---|
| 344 | | ``PermissionDenied`` and ``SystemExit``. Django catches these exceptions |
|---|
| 345 | | internally and converts them into the appropriate HTTP responses codes. |
|---|
| 346 | | |
|---|
| 347 | | Persistent state |
|---|
| 348 | | ~~~~~~~~~~~~~~~~ |
|---|
| 349 | | |
|---|
| 350 | | The Test Client is stateful; if a cookie is returned as part of a response, |
|---|
| 351 | | that cookie is provided as part of the next request issued by that Client |
|---|
| 352 | | instance. Expiry policies for these cookies are not followed; if you want |
|---|
| 353 | | a cookie to expire, either delete it manually or create a new Client |
|---|
| 354 | | instance (which will effectively delete all cookies). |
|---|
| 355 | | |
|---|
| 356 | | There are two properties of the Test Client which are used to store persistent |
|---|
| 357 | | state information. If necessary, these properties can be interrogated as |
|---|
| 358 | | part of a test condition. |
|---|
| 359 | | |
|---|
| 360 | | =============== ========================================================== |
|---|
| 361 | | Property Description |
|---|
| 362 | | =============== ========================================================== |
|---|
| 363 | | ``cookies`` A Python ``SimpleCookie`` object, containing the current |
|---|
| 364 | | values of all the client cookies. |
|---|
| 365 | | |
|---|
| 366 | | ``session`` A dictionary-like object containing session information. |
|---|
| 367 | | See the `session documentation`_ for full details. |
|---|
| 368 | | =============== ========================================================== |
|---|
| 369 | | |
|---|
| 370 | | .. _`session documentation`: ../sessions/ |
|---|
| 371 | | |
|---|
| 372 | | Example |
|---|
| 373 | | ~~~~~~~ |
|---|
| 374 | | |
|---|
| 375 | | The following is a simple unit test using the Test Client:: |
|---|
| 376 | | |
|---|
| 377 | | import unittest |
|---|
| 378 | | from django.test.client import Client |
|---|
| 379 | | |
|---|
| 380 | | class SimpleTest(unittest.TestCase): |
|---|
| 381 | | def setUp(self): |
|---|
| 382 | | # Every test needs a client |
|---|
| 383 | | self.client = Client() |
|---|
| 384 | | def test_details(self): |
|---|
| 385 | | # Issue a GET request |
|---|
| 386 | | response = self.client.get('/customer/details/') |
|---|
| 387 | | |
|---|
| 388 | | # Check that the respose is 200 OK |
|---|
| 389 | | self.failUnlessEqual(response.status_code, 200) |
|---|
| 390 | | # Check that the rendered context contains 5 customers |
|---|
| 391 | | self.failUnlessEqual(len(response.context['customers']), 5) |
|---|
| 392 | | |
|---|
| 393 | | TestCase |
|---|
| 394 | | -------- |
|---|
| 395 | | |
|---|
| 396 | | Normal python unit tests extend a base class of ``unittest.testCase``. |
|---|
| 397 | | Django provides an extension of this base class - ``django.test.TestCase`` |
|---|
| 398 | | - that provides some additional capabilities that can be useful for |
|---|
| 399 | | testing web sites. |
|---|
| 400 | | |
|---|
| 401 | | Moving from a normal unittest TestCase to a Django TestCase is easy - just |
|---|
| 402 | | change the base class of your test from ``unittest.TestCase`` to |
|---|
| 403 | | ``django.test.TestCase``. All of the standard Python unit test facilities |
|---|
| 404 | | will continue to be available, but they will be augmented with some useful |
|---|
| 405 | | extra facilities. |
|---|
| 406 | | |
|---|
| 407 | | Default Test Client |
|---|
| 408 | | ~~~~~~~~~~~~~~~~~~~ |
|---|
| 409 | | **New in Django development version** |
|---|
| 410 | | |
|---|
| 411 | | Every test case in a ``django.test.TestCase`` instance has access to an |
|---|
| 412 | | instance of a Django `Test Client`_. This Client can be accessed as |
|---|
| 413 | | ``self.client``. This client is recreated for each test. |
|---|
| 414 | | |
|---|
| 415 | | Fixture loading |
|---|
| 416 | | ~~~~~~~~~~~~~~~ |
|---|
| 417 | | |
|---|
| 418 | | A test case for a database-backed website isn't much use if there isn't any |
|---|
| 419 | | data in the database. To make it easy to put test data into the database, |
|---|
| 420 | | Django provides a fixtures framework. |
|---|
| 421 | | |
|---|
| 422 | | A *Fixture* is a collection of files that contain the serialized contents of |
|---|
| 423 | | the database. Each fixture has a unique name; however, the files that |
|---|
| 424 | | comprise the fixture can be distributed over multiple directories, in |
|---|
| 425 | | multiple applications. |
|---|
| 426 | | |
|---|
| 427 | | .. note:: |
|---|
| 428 | | If you have synchronized a Django project, you have already experienced |
|---|
| 429 | | the use of one fixture -- the ``initial_data`` fixture. Every time you |
|---|
| 430 | | synchronize the database, Django installs the ``initial_data`` fixture. |
|---|
| 431 | | This provides a mechanism to populate a new database with any initial |
|---|
| 432 | | data (such as a default set of categories). Fixtures with other names |
|---|
| 433 | | can be installed manually using ``django-admin.py loaddata``. |
|---|
| 434 | | |
|---|
| 435 | | However, for the purposes of unit testing, each test must be able to |
|---|
| 436 | | guarantee the contents of the database at the start of each and every |
|---|
| 437 | | test. |
|---|
| 438 | | |
|---|
| 439 | | To define a fixture for a test, all you need to do is add a class |
|---|
| 440 | | attribute to your test describing the fixtures you want the test to use. |
|---|
| 441 | | For example, the test case from `Writing unittests`_ would |
|---|
| 442 | | look like:: |
|---|
| 443 | | |
|---|
| 444 | | from django.test import TestCase |
|---|
| 445 | | from myapp.models import Animal |
|---|
| 446 | | |
|---|
| 447 | | class AnimalTestCase(TestCase): |
|---|
| 448 | | fixtures = ['mammals.json', 'birds'] |
|---|
| 449 | | |
|---|
| 450 | | def setUp(self): |
|---|
| 451 | | # test definitions as before |
|---|
| 452 | | |
|---|
| 453 | | def testFluffyAnimals(self): |
|---|
| 454 | | # A test that uses the fixtures |
|---|
| 455 | | |
|---|
| 456 | | At the start of each test case, before ``setUp()`` is run, Django will |
|---|
| 457 | | flush the database, returning the database the state it was in directly |
|---|
| 458 | | after ``syncdb`` was called. Then, all the named fixtures are installed. |
|---|
| 459 | | In this example, any JSON fixture called ``mammals``, and any fixture |
|---|
| 460 | | named ``birds`` will be installed. See the documentation on |
|---|
| 461 | | `loading fixtures`_ for more details on defining and installing fixtures. |
|---|
| 462 | | |
|---|
| 463 | | .. _`loading fixtures`: ../django-admin/#loaddata-fixture-fixture |
|---|
| 464 | | |
|---|
| 465 | | This flush/load procedure is repeated for each test in the test case, so you |
|---|
| 466 | | can be certain that the outcome of a test will not be affected by |
|---|
| 467 | | another test, or the order of test execution. |
|---|
| 468 | | |
|---|
| 469 | | Emptying the test outbox |
|---|
| 470 | | ~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 471 | | **New in Django development version** |
|---|
| 472 | | |
|---|
| 473 | | At the start of each test case, in addition to installing fixtures, |
|---|
| 474 | | Django clears the contents of the test e-mail outbox. |
|---|
| 475 | | |
|---|
| 476 | | For more detail on e-mail services during tests, see `E-mail services`_. |
|---|
| 477 | | |
|---|
| 478 | | Assertions |
|---|
| 479 | | ~~~~~~~~~~ |
|---|
| 480 | | **New in Django development version** |
|---|
| 481 | | |
|---|
| 482 | | Normal Python unit tests have a wide range of assertions, such as |
|---|
| 483 | | ``assertTrue`` and ``assertEquals`` that can be used to validate behavior. |
|---|
| 484 | | ``django.TestCase`` adds to these, providing some assertions |
|---|
| 485 | | that can be useful in testing the behavior of web sites. |
|---|
| 486 | | |
|---|
| 487 | | ``assertContains(response, text, count=None, status_code=200)`` |
|---|
| 488 | | Assert that a response indicates that a page could be retrieved and |
|---|
| 489 | | produced the nominated status code, and that ``text`` in the content |
|---|
| 490 | | of the response. If ``count`` is provided, ``text`` must occur exactly |
|---|
| 491 | | ``count`` times in the response. |
|---|
| 492 | | |
|---|
| 493 | | ``assertFormError(response, form, field, errors)`` |
|---|
| 494 | | Assert that a field on a form raised the provided list of errors when |
|---|
| 495 | | rendered on the form. |
|---|
| 496 | | |
|---|
| 497 | | ``form`` is the name the form object was given in the template context. |
|---|
| 498 | | |
|---|
| 499 | | ``field`` is the name of the field on the form to check. If ``field`` |
|---|
| 500 | | has a value of ``None``, non-field errors will be checked. |
|---|
| 501 | | |
|---|
| 502 | | ``errors`` is an error string, or a list of error strings, that are |
|---|
| 503 | | expected as a result of form validation. |
|---|
| 504 | | |
|---|
| 505 | | ``assertTemplateNotUsed(response, template_name)`` |
|---|
| 506 | | Assert that the template with the given name was *not* used in rendering |
|---|
| 507 | | the response. |
|---|
| 508 | | |
|---|
| 509 | | ``assertRedirects(response, expected_path, status_code=302, target_status_code=200)`` |
|---|
| 510 | | Assert that the response received produced the nominated status code, |
|---|
| 511 | | redirects the browser to the provided path, and that retrieving the provided |
|---|
| 512 | | path yields a response with the target status code. |
|---|
| 513 | | |
|---|
| 514 | | ``assertTemplateUsed(response, template_name)`` |
|---|
| 515 | | Assert that the template with the given name was used in rendering the |
|---|
| 516 | | response. |
|---|
| 517 | | |
|---|
| 518 | | E-mail services |
|---|
| 519 | | --------------- |
|---|
| 520 | | |
|---|
| 521 | | **New in Django development version** |
|---|
| 522 | | |
|---|
| 523 | | If your view makes use of the `Django e-mail services`_, you don't really |
|---|
| 524 | | want e-mail to be sent every time you run a test using that view. |
|---|
| 525 | | |
|---|
| 526 | | When the Django test framework is initialized, it transparently replaces the |
|---|
| 527 | | normal `SMTPConnection`_ class with a dummy implementation that redirects all |
|---|
| 528 | | e-mail to a dummy outbox. This outbox, stored as ``django.core.mail.outbox``, |
|---|
| 529 | | is a simple list of all `EmailMessage`_ instances that have been sent. |
|---|
| 530 | | For example, during test conditions, it would be possible to run the following |
|---|
| 531 | | code:: |
|---|
| 532 | | |
|---|
| 533 | | from django.core import mail |
|---|
| 534 | | |
|---|
| 535 | | # Send message |
|---|
| 536 | | mail.send_mail('Subject here', 'Here is the message.', 'from@example.com', |
|---|
| 537 | | ['to@example.com'], fail_silently=False) |
|---|
| 538 | | |
|---|
| 539 | | # One message has been sent |
|---|
| 540 | | self.assertEqual(len(mail.outbox), 1) |
|---|
| 541 | | # Subject of first message is correct |
|---|
| 542 | | self.assertEqual(mail.outbox[0].subject, 'Subject here') |
|---|
| 543 | | |
|---|
| 544 | | The ``mail.outbox`` object does not exist under normal execution conditions. |
|---|
| 545 | | The outbox is created during test setup, along with the dummy `SMTPConnection`_. |
|---|
| 546 | | When the test framework is torn down, the standard `SMTPConnection`_ class |
|---|
| 547 | | is restored, and the test outbox is destroyed. |
|---|
| 548 | | |
|---|
| 549 | | As noted `previously`_, the test outbox is emptied at the start of every |
|---|
| 550 | | test in a Django TestCase. To empty the outbox manually, assign the empty list |
|---|
| 551 | | to mail.outbox:: |
|---|
| 552 | | |
|---|
| 553 | | from django.core import mail |
|---|
| 554 | | |
|---|
| 555 | | # Empty the test outbox |
|---|
| 556 | | mail.outbox = [] |
|---|
| 557 | | |
|---|
| 558 | | .. _`Django e-mail services`: ../email/ |
|---|
| 559 | | .. _`SMTPConnection`: ../email/#the-emailmessage-and-smtpconnection-classes |
|---|
| 560 | | .. _`EmailMessage`: ../email/#the-emailmessage-and-smtpconnection-classes |
|---|
| 561 | | .. _`previously`: #emptying-the-test-outbox |
|---|
| 651 | | Using a different testing framework |
|---|
| 652 | | =================================== |
|---|
| 653 | | |
|---|
| 654 | | Doctest and Unittest are not the only Python testing frameworks. While |
|---|
| 655 | | Django doesn't provide explicit support these alternative frameworks, |
|---|
| 656 | | it does provide a mechanism to allow you to invoke tests constructed for |
|---|
| 657 | | an alternative framework as if they were normal Django tests. |
|---|
| | 353 | Testing tools |
|---|
| | 354 | ============= |
|---|
| | 355 | |
|---|
| | 356 | Django provides a small set of tools that come in handy when writing tests. |
|---|
| | 357 | |
|---|
| | 358 | The test client |
|---|
| | 359 | --------------- |
|---|
| | 360 | |
|---|
| | 361 | The test client is a Python class that acts as a dummy Web browser, allowing |
|---|
| | 362 | you to test your views and interact with your Django-powered application |
|---|
| | 363 | programatically. |
|---|
| | 364 | |
|---|
| | 365 | Some of the things you can do with the test client are: |
|---|
| | 366 | |
|---|
| | 367 | * Simulate GET and POST requests on a URL and observe the response -- |
|---|
| | 368 | everything from low-level HTTP (result headers and status codes) to |
|---|
| | 369 | page content. |
|---|
| | 370 | |
|---|
| | 371 | * Test that the correct view is executed for a given URL. |
|---|
| | 372 | |
|---|
| | 373 | * Test that a given request is rendered by a given Django template, with |
|---|
| | 374 | a template context that contains certain values. |
|---|
| | 375 | |
|---|
| | 376 | Note that the test client is not intended to be a replacement for Twill_, |
|---|
| | 377 | Selenium_, or other "in-browser" frameworks. Django's test client has |
|---|
| | 378 | a different focus. In short: |
|---|
| | 379 | |
|---|
| | 380 | * Use Django's test client to establish that the correct view is being |
|---|
| | 381 | called and that the view is collecting the correct context data. |
|---|
| | 382 | |
|---|
| | 383 | * Use in-browser frameworks such as Twill and Selenium to test *rendered* |
|---|
| | 384 | HTML and the *behavior* of Web pages, namely JavaScript functionality. |
|---|
| | 385 | |
|---|
| | 386 | A comprehensive test suite should use a combination of both test types. |
|---|
| | 387 | |
|---|
| | 388 | .. _Twill: http://twill.idyll.org/ |
|---|
| | 389 | .. _Selenium: http://www.openqa.org/selenium/ |
|---|
| | 390 | |
|---|
| | 391 | Overview and a quick example |
|---|
| | 392 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| | 393 | |
|---|
| | 394 | To use the test client, instantiate ``django.test.client.Client`` and retrieve |
|---|
| | 395 | Web pages:: |
|---|
| | 396 | |
|---|
| | 397 | >>> from django.test.client import Client |
|---|
| | 398 | >>> c = Client() |
|---|
| | 399 | >>> response = c.post('/login/', {'username': 'john', 'password': 'smith'}) |
|---|
| | 400 | >>> response.status_code |
|---|
| | 401 | 200 |
|---|
| | 402 | >>> response = c.get('/customer/details/') |
|---|
| | 403 | >>> response.content |
|---|
| | 404 | '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 ...' |
|---|
| | 405 | |
|---|
| | 406 | As this example suggests, you can instantiate ``Client`` from within a session |
|---|
| | 407 | of the Python interactive interpreter. |
|---|
| | 408 | |
|---|
| | 409 | Note a few important things about how the test client works: |
|---|
| | 410 | |
|---|
| | 411 | * The test client does *not* require the Web server to be running. In fact, |
|---|
| | 412 | it will run just fine with no Web server running at all! That's because |
|---|
| | 413 | it avoids the overhead of HTTP and deals directly with the Django |
|---|
| | 414 | framework. This helps make the unit tests run quickly. |
|---|
| | 415 | |
|---|
| | 416 | * When retrieving pages, remember to specify the *path* of the URL, not the |
|---|
| | 417 | whole domain. For example, this is correct:: |
|---|
| | 418 | |
|---|
| | 419 | >>> c.get('/login/') |
|---|
| | 420 | |
|---|
| | 421 | This is incorrect:: |
|---|
| | 422 | |
|---|
| | 423 | >>> c.get('http://www.example.com/login/') |
|---|
| | 424 | |
|---|
| | 425 | The test client is not capable of retrieving Web pages that are not |
|---|
| | 426 | powered by your Django project. If you need to retrieve other Web pages, |
|---|
| | 427 | use a Python standard library module such as urllib_ or urllib2_. |
|---|
| | 428 | |
|---|
| | 429 | * To resolve URLs, the test client uses whatever URLconf is pointed-to by |
|---|
| | 430 | your ``ROOT_URLCONF`` setting. |
|---|
| | 431 | |
|---|
| | 432 | * Although the above example would work in the Python interactive |
|---|
| | 433 | interpreter, some of the test client's functionality, notably the |
|---|
| | 434 | template-related functionality, is only available *while tests are running*. |
|---|
| | 435 | |
|---|
| | 436 | The reason for this is that Django's test runner performs a bit of black |
|---|
| | 437 | magic in order to determine which template was loaded by a given view. |
|---|
| | 438 | This black magic (essentially a patching of Django's template system in |
|---|
| | 439 | memory) only happens during test running. |
|---|
| | 440 | |
|---|
| | 441 | .. _urllib: http://docs.python.org/lib/module-urllib.html |
|---|
| | 442 | .. _urllib2: http://docs.python.org/lib/module-urllib2.html |
|---|
| | 443 | |
|---|
| | 444 | Making requests |
|---|
| | 445 | ~~~~~~~~~~~~~~~ |
|---|
| | 446 | |
|---|
| | 447 | Use the ``django.test.client.Client`` class to make requests. It requires no |
|---|
| | 448 | arguments at time of construction:: |
|---|
| | 449 | |
|---|
| | 450 | >>> c = Client() |
|---|
| | 451 | |
|---|
| | 452 | Once you have a ``Client`` instance, you can call any of the following methods: |
|---|
| | 453 | |
|---|
| | 454 | ``get(path, data={})`` |
|---|
| | 455 | Makes a GET request on the provided ``path`` and returns a ``Response`` |
|---|
| | 456 | object, which is documented below. |
|---|
| | 457 | |
|---|
| | 458 | The key-value pairs in the ``data`` dictionary are used to create a GET |
|---|
| | 459 | data payload. For example:: |
|---|
| | 460 | |
|---|
| | 461 | >>> c = Client() |
|---|
| | 462 | >>> c.get('/customers/details/', {'name': 'fred', 'age': 7}) |
|---|
| | 463 | |
|---|
| | 464 | ...will result in the evaluation of a GET request equivalent to:: |
|---|
| | 465 | |
|---|
| | 466 | /customers/details/?name=fred&age=7 |
|---|
| | 467 | |
|---|
| | 468 | ``post(path, data={}, content_type=MULTIPART_CONTENT)`` |
|---|
| | 469 | Makes a POST request on the provided ``path`` and returns a ``Response`` |
|---|
| | 470 | object, which is documented below. |
|---|
| | 471 | |
|---|
| | 472 | The key-value pairs in the ``data`` dictionary are used to submit POST |
|---|
| | 473 | data. For example:: |
|---|
| | 474 | |
|---|
| | 475 | >>> c = Client() |
|---|
| | 476 | >>> c.get('/login/', {'name': 'fred', 'passwd': 'secret'}) |
|---|
| | 477 | |
|---|
| | 478 | ...will result in the evaluation of a POST request to this URL:: |
|---|
| | 479 | |
|---|
| | 480 | /login/ |
|---|
| | 481 | |
|---|
| | 482 | ...with this POST data:: |
|---|
| | 483 | |
|---|
| | 484 | name=fred&passwd&secret |
|---|
| | 485 | |
|---|
| | 486 | If you provide ``content_type`` (e.g., ``text/xml`` for an XML payload), |
|---|
| | 487 | the contents of ``data`` will be sent as-is in the POST request, using |
|---|
| | 488 | ``content_type`` in the HTTP ``Content-Type`` header. |
|---|
| | 489 | |
|---|
| | 490 | If you don't provide a value for ``content_type``, the values in |
|---|
| | 491 | ``data`` will be transmitted with a content type of ``multipart/form-data``. |
|---|
| | 492 | In this case, the key-value pairs in ``data`` will be encoded as a |
|---|
| | 493 | multipart message and used to create the POST data payload. |
|---|
| | 494 | |
|---|
| | 495 | To submit multiple values for a given key -- for example, to specify |
|---|
| | 496 | the selections for a ``<select multiple>`` -- provide the values as a |
|---|
| | 497 | list or tuple for the required key. For example, this value of ``data`` |
|---|
| | 498 | would submit three selected values for the field named ``choices``:: |
|---|
| | 499 | |
|---|
| | 500 | {'choices': ('a', 'b', 'd')} |
|---|
| | 501 | |
|---|
| | 502 | Submitting files is a special case. To POST a file, you need only provide |
|---|
| | 503 | the file field name as a key, and a file handle to the file you wish to |
|---|
| | 504 | upload as a value. For example:: |
|---|
| | 505 | |
|---|
| | 506 | >>> c = Client() |
|---|
| | 507 | >>> f = open('wishlist.doc') |
|---|
| | 508 | >>> c.post('/customers/wishes/', {'name': 'fred', 'attachment': f}) |
|---|
| | 509 | >>> f.close() |
|---|
| | 510 | |
|---|
| | 511 | (The name ``attachment`` here is not relevant; use whatever name your |
|---|
| | 512 | file-processing code expects.) |
|---|
| | 513 | |
|---|
| | 514 | Note that you should manually close the file after it has been provided to |
|---|
| | 515 | ``post()``. |
|---|
| | 516 | |
|---|
| | 517 | ``login(**credentials)`` |
|---|
| | 518 | **New in Django development version** |
|---|
| | 519 | |
|---|
| | 520 | If your site uses Django's `authentication system`_ and you deal with |
|---|
| | 521 | logging in users, you can use the test client's ``login()`` method to |
|---|
| | 522 | simulate the effect of a user logging into the site. |
|---|
| | 523 | |
|---|
| | 524 | After you call this method, the test client will have all the cookies and |
|---|
| | 525 | session data required to pass any login-based tests that may form part of |
|---|
| | 526 | a view. |
|---|
| | 527 | |
|---|
| | 528 | The format of the ``credentials`` argument depends on which |
|---|
| | 529 | `authentication backend`_ you're using (which is configured by your |
|---|
| | 530 | ``AUTHENTICATION_BACKENDS`` setting). If you're using the standard |
|---|
| | 531 | authentication backend provided by Django (``ModelBackend``), |
|---|
| | 532 | ``credentials`` should be the user's username and password, provided as |
|---|
| | 533 | keyword arguments:: |
|---|
| | 534 | |
|---|
| | 535 | >>> c = Client() |
|---|
| | 536 | >>> c.login(username='fred', password='secret') |
|---|
| | 537 | >>> # Now you can access a view that's only available to logged-in users. |
|---|
| | 538 | |
|---|
| | 539 | If you're using a different authentication backend, this method may require |
|---|
| | 540 | different credentials. It requires whichever credentials are required by |
|---|
| | 541 | your backend's ``authenticate()`` method. |
|---|
| | 542 | |
|---|
| | 543 | ``login()`` returns ``True`` if it the credentials were accepted and login |
|---|
| | 544 | was successful. |
|---|
| | 545 | |
|---|
| | 546 | Finally, you'll need to remember to create user accounts before you can use |
|---|
| | 547 | this method. As we explained above, the test runner is executed using a |
|---|
| | 548 | test database, which contains no users by default. As a result, user |
|---|
| | 549 | accounts that are valid on your production site will not work under test |
|---|
| | 550 | conditions. You'll need to create users as part of the test suite -- either |
|---|
| | 551 | manually (using the Django model API) or with a test fixture. |
|---|
| | 552 | |
|---|
| | 553 | .. _authentication system: ../authentication/ |
|---|
| | 554 | .. _authentication backend: ../authentication/#other-authentication-sources |
|---|
| | 555 | |
|---|
| | 556 | Testing responses |
|---|
| | 557 | ~~~~~~~~~~~~~~~~~ |
|---|
| | 558 | |
|---|
| | 559 | The ``get()`` and ``post()`` methods both return a ``Response`` object. This |
|---|
| | 560 | ``Response`` object is *not* the same as the ``HttpResponse`` object returned |
|---|
| | 561 | Django views; this object is simpler and has some additional data useful for |
|---|
| | 562 | tests. |
|---|
| | 563 | |
|---|
| | 564 | Specifically, a ``Response`` object has the following attributes:: |
|---|
| | 565 | |
|---|
| | 566 | =============== ========================================================== |
|---|
| | 567 | Attribute Description |
|---|
| | 568 | =============== ========================================================== |
|---|
| | 569 | ``status_code`` The HTTP status of the response, as an integer. See |
|---|
| | 570 | RFC2616_ for a full list of HTTP status codes. |
|---|
| | 571 | |
|---|
| | 572 | ``content`` The body of the response, as a string. This is the final |
|---|
| | 573 | page content as rendered by the view, or any error |
|---|
| | 574 | message (such as the URL for a 302 redirect). |
|---|
| | 575 | |
|---|
| | 576 | ``template`` The ``Template`` instance that was used to render the |
|---|
| | 577 | final content. Use ``template.name`` to get the |
|---|
| | 578 | template's file name, if the template was loaded from a |
|---|
| | 579 | file. (The name is a string such as |
|---|
| | 580 | ``'admin/index.html'``.) |
|---|
| | 581 | |
|---|
| | 582 | If the rendered page used multiple templates -- e.g., |
|---|
| | 583 | using `template inheritance`_ -- then ``template`` will |
|---|
| | 584 | be a list of ``Template`` instances, in the order in |
|---|
| | 585 | which they were rendered. |
|---|
| | 586 | |
|---|
| | 587 | ``context`` The template ``Context`` instance that was used to render |
|---|
| | 588 | the template that produced the response content. |
|---|
| | 589 | |
|---|
| | 590 | As with ``template``, if the rendered page used multiple |
|---|
| | 591 | templates, then ``context`` will be a list of ``Context`` |
|---|
| | 592 | objects, in the order in which they were rendered. |
|---|
| | 593 | =============== ========================================================== |
|---|
| | 594 | |
|---|
| | 595 | .. _RFC2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html |
|---|
| | 596 | .. _template inheritance: ../templates/#template-inheritance |
|---|
| | 597 | |
|---|
| | 598 | Exceptions |
|---|
| | 599 | ~~~~~~~~~~ |
|---|
| | 600 | |
|---|
| | 601 | If you point the test client at a view that raises an exception, that exception |
|---|
| | 602 | will be visible in the test case. You can then use a standard ``try...catch`` |
|---|
| | 603 | block or ``unittest.TestCase.assertRaises()`` to test for exceptions. |
|---|
| | 604 | |
|---|
| | 605 | The only exceptions that are not visible to the test client are ``Http404``, |
|---|
| | 606 | ``PermissionDenied`` and ``SystemExit``. Django catches these exceptions |
<