123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699 |
- =======================
- Advanced testing topics
- =======================
- The request factory
- ===================
- .. currentmodule:: django.test
- .. class:: RequestFactory
- The :class:`~django.test.RequestFactory` shares the same API as
- the test client. However, instead of behaving like a browser, the
- RequestFactory provides a way to generate a request instance that can
- be used as the first argument to any view. This means you can test a
- view function the same way as you would test any other function -- as
- a black box, with exactly known inputs, testing for specific outputs.
- The API for the :class:`~django.test.RequestFactory` is a slightly
- restricted subset of the test client API:
- * It only has access to the HTTP methods :meth:`~Client.get()`,
- :meth:`~Client.post()`, :meth:`~Client.put()`,
- :meth:`~Client.delete()`, :meth:`~Client.head()`,
- :meth:`~Client.options()`, and :meth:`~Client.trace()`.
- * These methods accept all the same arguments *except* for
- ``follows``. Since this is just a factory for producing
- requests, it's up to you to handle the response.
- * It does not support middleware. Session and authentication
- attributes must be supplied by the test itself if required
- for the view to function properly.
- Example
- -------
- The following is a simple unit test using the request factory::
- from django.contrib.auth.models import AnonymousUser, User
- from django.test import TestCase, RequestFactory
- from .views import MyView, my_view
- class SimpleTest(TestCase):
- def setUp(self):
- # Every test needs access to the request factory.
- self.factory = RequestFactory()
- self.user = User.objects.create_user(
- username='jacob', email='jacob@…', password='top_secret')
- def test_details(self):
- # Create an instance of a GET request.
- request = self.factory.get('/customer/details')
- # Recall that middleware are not supported. You can simulate a
- # logged-in user by setting request.user manually.
- request.user = self.user
- # Or you can simulate an anonymous user by setting request.user to
- # an AnonymousUser instance.
- request.user = AnonymousUser()
- # Test my_view() as if it were deployed at /customer/details
- response = my_view(request)
- # Use this syntax for class-based views.
- response = MyView.as_view()(request)
- self.assertEqual(response.status_code, 200)
- .. _topics-testing-advanced-multiple-hosts:
- Tests and multiple host names
- =============================
- The :setting:`ALLOWED_HOSTS` setting is validated when running tests. This
- allows the test client to differentiate between internal and external URLs.
- Projects that support multitenancy or otherwise alter business logic based on
- the request's host and use custom host names in tests must include those hosts
- in :setting:`ALLOWED_HOSTS`.
- The first and simplest option to do so is to add the hosts to your settings
- file. For example, the test suite for docs.djangoproject.com includes the
- following::
- from django.test import TestCase
- class SearchFormTestCase(TestCase):
- def test_empty_get(self):
- response = self.client.get('/en/dev/search/', HTTP_HOST='docs.djangoproject.dev:8000')
- self.assertEqual(response.status_code, 200)
- and the settings file includes a list of the domains supported by the project::
- ALLOWED_HOSTS = [
- 'www.djangoproject.dev',
- 'docs.djangoproject.dev',
- ...
- ]
- Another option is to add the required hosts to :setting:`ALLOWED_HOSTS` using
- :meth:`~django.test.override_settings()` or
- :attr:`~django.test.SimpleTestCase.modify_settings()`. This option may be
- preferable in standalone apps that can't package their own settings file or
- for projects where the list of domains is not static (e.g., subdomains for
- multitenancy). For example, you could write a test for the domain
- ``http://otherserver/`` as follows::
- from django.test import TestCase, override_settings
- class MultiDomainTestCase(TestCase):
- @override_settings(ALLOWED_HOSTS=['otherserver'])
- def test_other_domain(self):
- response = self.client.get('http://otherserver/foo/bar/')
- Disabling :setting:`ALLOWED_HOSTS` checking (``ALLOWED_HOSTS = ['*']``) when
- running tests prevents the test client from raising a helpful error message if
- you follow a redirect to an external URL.
- .. versionchanged:: 1.11
- Older versions didn't validate ``ALLOWED_HOSTS`` while testing so these
- techniques weren't necessary.
- .. _topics-testing-advanced-multidb:
- Tests and multiple databases
- ============================
- .. _topics-testing-primaryreplica:
- Testing primary/replica configurations
- --------------------------------------
- If you're testing a multiple database configuration with primary/replica
- (referred to as master/slave by some databases) replication, this strategy of
- creating test databases poses a problem.
- When the test databases are created, there won't be any replication,
- and as a result, data created on the primary won't be seen on the
- replica.
- To compensate for this, Django allows you to define that a database is
- a *test mirror*. Consider the following (simplified) example database
- configuration::
- DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.mysql',
- 'NAME': 'myproject',
- 'HOST': 'dbprimary',
- # ... plus some other settings
- },
- 'replica': {
- 'ENGINE': 'django.db.backends.mysql',
- 'NAME': 'myproject',
- 'HOST': 'dbreplica',
- 'TEST': {
- 'MIRROR': 'default',
- },
- # ... plus some other settings
- }
- }
- In this setup, we have two database servers: ``dbprimary``, described
- by the database alias ``default``, and ``dbreplica`` described by the
- alias ``replica``. As you might expect, ``dbreplica`` has been configured
- by the database administrator as a read replica of ``dbprimary``, so in
- normal activity, any write to ``default`` will appear on ``replica``.
- If Django created two independent test databases, this would break any
- tests that expected replication to occur. However, the ``replica``
- database has been configured as a test mirror (using the
- :setting:`MIRROR <TEST_MIRROR>` test setting), indicating that under
- testing, ``replica`` should be treated as a mirror of ``default``.
- When the test environment is configured, a test version of ``replica``
- will *not* be created. Instead the connection to ``replica``
- will be redirected to point at ``default``. As a result, writes to
- ``default`` will appear on ``replica`` -- but because they are actually
- the same database, not because there is data replication between the
- two databases.
- .. _topics-testing-creation-dependencies:
- Controlling creation order for test databases
- ---------------------------------------------
- By default, Django will assume all databases depend on the ``default``
- database and therefore always create the ``default`` database first.
- However, no guarantees are made on the creation order of any other
- databases in your test setup.
- If your database configuration requires a specific creation order, you
- can specify the dependencies that exist using the :setting:`DEPENDENCIES
- <TEST_DEPENDENCIES>` test setting. Consider the following (simplified)
- example database configuration::
- DATABASES = {
- 'default': {
- # ... db settings
- 'TEST': {
- 'DEPENDENCIES': ['diamonds'],
- },
- },
- 'diamonds': {
- ... db settings
- 'TEST': {
- 'DEPENDENCIES': [],
- },
- },
- 'clubs': {
- # ... db settings
- 'TEST': {
- 'DEPENDENCIES': ['diamonds'],
- },
- },
- 'spades': {
- # ... db settings
- 'TEST': {
- 'DEPENDENCIES': ['diamonds', 'hearts'],
- },
- },
- 'hearts': {
- # ... db settings
- 'TEST': {
- 'DEPENDENCIES': ['diamonds', 'clubs'],
- },
- }
- }
- Under this configuration, the ``diamonds`` database will be created first,
- as it is the only database alias without dependencies. The ``default`` and
- ``clubs`` alias will be created next (although the order of creation of this
- pair is not guaranteed), then ``hearts``, and finally ``spades``.
- If there are any circular dependencies in the :setting:`DEPENDENCIES
- <TEST_DEPENDENCIES>` definition, an
- :exc:`~django.core.exceptions.ImproperlyConfigured` exception will be raised.
- Advanced features of ``TransactionTestCase``
- ============================================
- .. attribute:: TransactionTestCase.available_apps
- .. warning::
- This attribute is a private API. It may be changed or removed without
- a deprecation period in the future, for instance to accommodate changes
- in application loading.
- It's used to optimize Django's own test suite, which contains hundreds
- of models but no relations between models in different applications.
- By default, ``available_apps`` is set to ``None``. After each test, Django
- calls :djadmin:`flush` to reset the database state. This empties all tables
- and emits the :data:`~django.db.models.signals.post_migrate` signal, which
- re-creates one content type and three permissions for each model. This
- operation gets expensive proportionally to the number of models.
- Setting ``available_apps`` to a list of applications instructs Django to
- behave as if only the models from these applications were available. The
- behavior of ``TransactionTestCase`` changes as follows:
- - :data:`~django.db.models.signals.post_migrate` is fired before each
- test to create the content types and permissions for each model in
- available apps, in case they're missing.
- - After each test, Django empties only tables corresponding to models in
- available apps. However, at the database level, truncation may cascade to
- related models in unavailable apps. Furthermore
- :data:`~django.db.models.signals.post_migrate` isn't fired; it will be
- fired by the next ``TransactionTestCase``, after the correct set of
- applications is selected.
- Since the database isn't fully flushed, if a test creates instances of
- models not included in ``available_apps``, they will leak and they may
- cause unrelated tests to fail. Be careful with tests that use sessions;
- the default session engine stores them in the database.
- Since :data:`~django.db.models.signals.post_migrate` isn't emitted after
- flushing the database, its state after a ``TransactionTestCase`` isn't the
- same as after a ``TestCase``: it's missing the rows created by listeners
- to :data:`~django.db.models.signals.post_migrate`. Considering the
- :ref:`order in which tests are executed <order-of-tests>`, this isn't an
- issue, provided either all ``TransactionTestCase`` in a given test suite
- declare ``available_apps``, or none of them.
- ``available_apps`` is mandatory in Django's own test suite.
- .. attribute:: TransactionTestCase.reset_sequences
- Setting ``reset_sequences = True`` on a ``TransactionTestCase`` will make
- sure sequences are always reset before the test run::
- class TestsThatDependsOnPrimaryKeySequences(TransactionTestCase):
- reset_sequences = True
- def test_animal_pk(self):
- lion = Animal.objects.create(name="lion", sound="roar")
- # lion.pk is guaranteed to always be 1
- self.assertEqual(lion.pk, 1)
- Unless you are explicitly testing primary keys sequence numbers, it is
- recommended that you do not hard code primary key values in tests.
- Using ``reset_sequences = True`` will slow down the test, since the primary
- key reset is an relatively expensive database operation.
- .. _testing-reusable-applications:
- Using the Django test runner to test reusable applications
- ==========================================================
- If you are writing a :doc:`reusable application </intro/reusable-apps>`
- you may want to use the Django test runner to run your own test suite
- and thus benefit from the Django testing infrastructure.
- A common practice is a *tests* directory next to the application code, with the
- following structure::
- runtests.py
- polls/
- __init__.py
- models.py
- ...
- tests/
- __init__.py
- models.py
- test_settings.py
- tests.py
- Let's take a look inside a couple of those files:
- .. snippet::
- :filename: runtests.py
- #!/usr/bin/env python
- import os
- import sys
- import django
- from django.conf import settings
- from django.test.utils import get_runner
- if __name__ == "__main__":
- os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.test_settings'
- django.setup()
- TestRunner = get_runner(settings)
- test_runner = TestRunner()
- failures = test_runner.run_tests(["tests"])
- sys.exit(bool(failures))
- This is the script that you invoke to run the test suite. It sets up the
- Django environment, creates the test database and runs the tests.
- For the sake of clarity, this example contains only the bare minimum
- necessary to use the Django test runner. You may want to add
- command-line options for controlling verbosity, passing in specific test
- labels to run, etc.
- .. snippet::
- :filename: tests/test_settings.py
- SECRET_KEY = 'fake-key'
- INSTALLED_APPS = [
- "tests",
- ]
- This file contains the :doc:`Django settings </topics/settings>`
- required to run your app's tests.
- Again, this is a minimal example; your tests may require additional
- settings to run.
- Since the *tests* package is included in :setting:`INSTALLED_APPS` when
- running your tests, you can define test-only models in its ``models.py``
- file.
- .. _other-testing-frameworks:
- Using different testing frameworks
- ==================================
- Clearly, :mod:`unittest` is not the only Python testing framework. 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.runner.DiscoverRunner'``. This class defines the default Django
- testing behavior. This behavior involves:
- #. Performing global pre-test setup.
- #. Looking for tests in any file below the current directory whose name matches
- the pattern ``test*.py``.
- #. Creating the test databases.
- #. Running ``migrate`` to install models and initial data into the test
- databases.
- #. Running the tests that were found.
- #. Destroying the test databases.
- #. Performing global post-test teardown.
- If you define your own test runner class and point :setting:`TEST_RUNNER` at
- that class, 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, or to modify the Django test execution
- process to satisfy whatever testing requirements you may have.
- .. _topics-testing-test_runner:
- Defining a test runner
- ----------------------
- .. currentmodule:: django.test.runner
- A test runner is a class defining a ``run_tests()`` method. Django ships
- with a ``DiscoverRunner`` class that defines the default Django testing
- behavior. This class defines the ``run_tests()`` entry point, plus a
- selection of other methods that are used to by ``run_tests()`` to set up,
- execute and tear down the test suite.
- .. class:: DiscoverRunner(pattern='test*.py', top_level=None, verbosity=1, interactive=True, failfast=False, keepdb=False, reverse=False, debug_sql=False, **kwargs)
- ``DiscoverRunner`` will search for tests in any file matching ``pattern``.
- ``top_level`` can be used to specify the directory containing your
- top-level Python modules. Usually Django can figure this out automatically,
- so it's not necessary to specify this option. If specified, it should
- generally be the directory containing your ``manage.py`` file.
- ``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.
- If ``failfast`` is ``True``, the test suite will stop running after the
- first test failure is detected.
- If ``keepdb`` is ``True``, the test suite will use the existing database,
- or create one if necessary. If ``False``, a new database will be created,
- prompting the user to remove the existing one, if present.
- If ``reverse`` is ``True``, test cases will be executed in the opposite
- order. This could be useful to debug tests that aren't properly isolated
- and have side effects. :ref:`Grouping by test class <order-of-tests>` is
- preserved when using this option.
- If ``debug_sql`` is ``True``, failing test cases will output SQL queries
- logged to the :ref:`django.db.backends logger <django-db-logger>` as well
- as the traceback. If ``verbosity`` is ``2``, then queries in all tests are
- output.
- Django may, from time to time, extend the capabilities of the test runner
- by adding new arguments. The ``**kwargs`` declaration allows for this
- expansion. If you subclass ``DiscoverRunner`` or write your own test
- runner, ensure it accepts ``**kwargs``.
- Your test runner may also define additional command-line options.
- Create or override an ``add_arguments(cls, parser)`` class method and add
- custom arguments by calling ``parser.add_argument()`` inside the method, so
- that the :djadmin:`test` command will be able to use those arguments.
- Attributes
- ~~~~~~~~~~
- .. attribute:: DiscoverRunner.test_suite
- The class used to build the test suite. By default it is set to
- ``unittest.TestSuite``. This can be overridden if you wish to implement
- different logic for collecting tests.
- .. attribute:: DiscoverRunner.test_runner
- This is the class of the low-level test runner which is used to execute
- the individual tests and format the results. By default it is set to
- ``unittest.TextTestRunner``. Despite the unfortunate similarity in
- naming conventions, this is not the same type of class as
- ``DiscoverRunner``, which covers a broader set of responsibilities. You
- can override this attribute to modify the way tests are run and reported.
- .. attribute:: DiscoverRunner.test_loader
- This is the class that loads tests, whether from TestCases or modules or
- otherwise and bundles them into test suites for the runner to execute.
- By default it is set to ``unittest.defaultTestLoader``. You can override
- this attribute if your tests are going to be loaded in unusual ways.
- Methods
- ~~~~~~~
- .. method:: DiscoverRunner.run_tests(test_labels, extra_tests=None, **kwargs)
- Run the test suite.
- ``test_labels`` allows you to specify which tests to run and supports
- several formats (see :meth:`DiscoverRunner.build_suite` for a list of
- supported formats).
- ``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 ``test_labels``.
- This method should return the number of tests that failed.
- .. classmethod:: DiscoverRunner.add_arguments(parser)
- Override this class method to add custom arguments accepted by the
- :djadmin:`test` management command. See
- :py:meth:`argparse.ArgumentParser.add_argument()` for details about adding
- arguments to a parser.
- .. method:: DiscoverRunner.setup_test_environment(**kwargs)
- Sets up the test environment by calling
- :func:`~django.test.utils.setup_test_environment` and setting
- :setting:`DEBUG` to ``False``.
- .. method:: DiscoverRunner.build_suite(test_labels, extra_tests=None, **kwargs)
- Constructs a test suite that matches the test labels provided.
- ``test_labels`` is a list of strings describing the tests to be run. A test
- label can take one of four forms:
- * ``path.to.test_module.TestCase.test_method`` -- Run a single test method
- in a test case.
- * ``path.to.test_module.TestCase`` -- Run all the test methods in a test
- case.
- * ``path.to.module`` -- Search for and run all tests in the named Python
- package or module.
- * ``path/to/directory`` -- Search for and run all tests below the named
- directory.
- If ``test_labels`` has a value of ``None``, the test runner will search for
- tests in all files below the current directory whose names match its
- ``pattern`` (see above).
- ``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 ``test_labels``.
- Returns a ``TestSuite`` instance ready to be run.
- .. method:: DiscoverRunner.setup_databases(**kwargs)
- Creates the test databases.
- Returns a data structure that provides enough detail to undo the changes
- that have been made. This data will be provided to the ``teardown_databases()``
- function at the conclusion of testing.
- .. method:: DiscoverRunner.run_suite(suite, **kwargs)
- Runs the test suite.
- Returns the result produced by the running the test suite.
- .. method:: DiscoverRunner.teardown_databases(old_config, **kwargs)
- Destroys the test databases, restoring pre-test conditions.
- ``old_config`` is a data structure defining the changes in the
- database configuration that need to be reversed. It is the return
- value of the ``setup_databases()`` method.
- .. method:: DiscoverRunner.teardown_test_environment(**kwargs)
- Restores the pre-test environment.
- .. method:: DiscoverRunner.suite_result(suite, result, **kwargs)
- Computes and returns a return code based on a test suite, and the result
- from that test suite.
- Testing utilities
- -----------------
- ``django.test.utils``
- ~~~~~~~~~~~~~~~~~~~~~
- .. 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 email outbox.
- .. function:: teardown_test_environment()
- Performs any global post-test teardown, such as removing the black
- magic hooks into the template system and restoring normal email
- services.
- ``django.db.connection.creation``
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- .. currentmodule:: django.db.connection.creation
- The creation module of the database backend also provides some utilities that
- can be useful during testing.
- .. function:: create_test_db(verbosity=1, autoclobber=False, serialize=True, keepdb=False)
- Creates a new test database and runs ``migrate`` 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.
- ``serialize`` determines if Django serializes the database into an
- in-memory JSON string before running tests (used to restore the database
- state between tests if you don't have transactions). You can set this to
- ``False`` to speed up creation time if you don't have any test classes
- with :ref:`serialized_rollback=True <test-case-serialized-rollback>`.
- If you are using the default test runner, you can control this with the
- the :setting:`SERIALIZE <TEST_SERIALIZE>` entry in the :setting:`TEST
- <DATABASE-TEST>` dictionary.
- ``keepdb`` determines if the test run should use an existing
- database, or create a new one. If ``True``, the existing
- database will be used, or created if not present. If ``False``,
- a new database will be created, prompting the user to remove
- the existing one, if present.
- Returns the name of the test database that it created.
- ``create_test_db()`` has the side effect of modifying the value of
- :setting:`NAME` in :setting:`DATABASES` to match the name of the test
- database.
- .. function:: destroy_test_db(old_database_name, verbosity=1, keepdb=False)
- Destroys the database whose name is the value of :setting:`NAME` in
- :setting:`DATABASES`, and sets :setting:`NAME` to the value of
- ``old_database_name``.
- The ``verbosity`` argument has the same behavior as for
- :class:`~django.test.runner.DiscoverRunner`.
- If the ``keepdb`` argument is ``True``, then the connection to the
- database will be closed, but the database will not be destroyed.
- .. _topics-testing-code-coverage:
- Integration with ``coverage.py``
- ================================
- Code coverage describes how much source code has been tested. It shows which
- parts of your code are being exercised by tests and which are not. It's an
- important part of testing applications, so it's strongly recommended to check
- the coverage of your tests.
- Django can be easily integrated with `coverage.py`_, a tool for measuring code
- coverage of Python programs. First, `install coverage.py`_. Next, run the
- following from your project folder containing ``manage.py``::
- coverage run --source='.' manage.py test myapp
- This runs your tests and collects coverage data of the executed files in your
- project. You can see a report of this data by typing following command::
- coverage report
- Note that some Django code was executed while running tests, but it is not
- listed here because of the ``source`` flag passed to the previous command.
- For more options like annotated HTML listings detailing missed lines, see the
- `coverage.py`_ docs.
- .. _coverage.py: http://nedbatchelder.com/code/coverage/
- .. _install coverage.py: https://pypi.python.org/pypi/coverage
|