diff --git a/docs/internals/contributing/writing-code/unit-tests.txt b/docs/internals/contributing/writing-code/unit-tests.txt index d6a031b3ae..36f767614a 100644 --- a/docs/internals/contributing/writing-code/unit-tests.txt +++ b/docs/internals/contributing/writing-code/unit-tests.txt @@ -13,8 +13,8 @@ The tests cover: We appreciate any and all contributions to the test suite! The Django tests all use the testing infrastructure that ships with Django for -testing applications. See :doc:`Testing Django applications -` for an explanation of how to write new tests. +testing applications. See :doc:`/topics/testing/overview` for an explanation of +how to write new tests. .. _running-unit-tests: diff --git a/docs/intro/contributing.txt b/docs/intro/contributing.txt index 8a16a9b0d0..7250f9e676 100644 --- a/docs/intro/contributing.txt +++ b/docs/intro/contributing.txt @@ -281,7 +281,7 @@ correctly in a couple different situations. computer programming, so there's lots of information out there: * A good first look at writing tests for Django can be found in the - documentation on :doc:`Testing Django applications `. + documentation on :doc:`/topics/testing/overview`. * Dive Into Python (a free online book for beginning Python developers) includes a great `introduction to Unit Testing`__. * After reading those, if you want something a little meatier to sink diff --git a/docs/topics/email.txt b/docs/topics/email.txt index 058a007721..6b93d7d2b8 100644 --- a/docs/topics/email.txt +++ b/docs/topics/email.txt @@ -629,6 +629,5 @@ the email body. You then only need to set the :setting:`EMAIL_HOST` and :setting:`EMAIL_PORT` accordingly. For a more detailed discussion of SMTP server options, see the Python documentation for the :mod:`smtpd` module. -For information about unit-testing the sending of emails in your -application, see the :ref:`topics-testing-email` section of :doc:`Testing -Django applications `. +For information about unit-testing the sending of emails in your application, +see the :ref:`topics-testing-email` section of the testing documentation. diff --git a/docs/topics/testing/index.txt b/docs/topics/testing/index.txt index 1a99a399b4..e8cab96277 100644 --- a/docs/topics/testing/index.txt +++ b/docs/topics/testing/index.txt @@ -2,12 +2,6 @@ Testing in Django ================= -.. toctree:: - :hidden: - - overview - advanced - 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: @@ -28,9 +22,6 @@ it should be doing. The best part is, it's really easy. -Where to go from here -===================== - The preferred way to write tests in Django is using the :mod:`unittest` module built in to the Python standard library. This is covered in detail in the :doc:`overview` document. @@ -38,3 +29,10 @@ built in to the Python standard library. This is covered in detail in the You can also use any *other* Python test framework; Django provides an API and tools for that kind of integration. They are described in the :ref:`other-testing-frameworks` section of :doc:`advanced`. + +.. toctree:: + :maxdepth: 1 + + overview + tools + advanced diff --git a/docs/topics/testing/overview.txt b/docs/topics/testing/overview.txt index 3bdb13c28f..078c2a1da9 100644 --- a/docs/topics/testing/overview.txt +++ b/docs/topics/testing/overview.txt @@ -1,14 +1,15 @@ -=========================== -Testing Django applications -=========================== +========================= +Writing and running tests +========================= .. module:: django.test :synopsis: Testing tools for Django applications. .. seealso:: - The :doc:`testing tutorial ` and the - :doc:`advanced testing topics `. + The :doc:`testing tutorial `, the :doc:`testing tools + reference `, and the :doc:`advanced testing topics + `. This document is split into two primary sections. First, we explain how to write tests with Django. Then, we explain how to run them. @@ -302,1597 +303,3 @@ to a faster hashing algorithm:: Don't forget to also include in :setting:`PASSWORD_HASHERS` any hashing algorithm used in fixtures, if any. - -Testing tools -============= - -Django provides a small set of tools that come in handy when writing tests. - -.. _test-client: - -The 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. - -* See the chain of redirects (if any) and check the URL and status code at - each step. - -* 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 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 template is being - rendered and that the template is passed the correct context data. - -* Use in-browser frameworks like Selenium_ to test *rendered* HTML and the - *behavior* of Web pages, namely JavaScript functionality. Django also - provides special support for those frameworks; see the section on - :class:`~django.test.LiveServerTestCase` for more details. - -A comprehensive test suite should use a combination of both test types. - -Overview and a quick example -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To use the test client, instantiate ``django.test.Client`` and retrieve -Web pages:: - - >>> from django.test import Client - >>> c = Client() - >>> response = c.post('/login/', {'username': 'john', 'password': 'smith'}) - >>> response.status_code - 200 - >>> response = c.get('/customer/details/') - >>> response.content - '>> 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 :mod:`urllib` or - :mod:`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. - -* By default, the test client will disable any CSRF checks - performed by your site. - - If, for some reason, you *want* the test client to perform CSRF - checks, you can create an instance of the test client that - enforces CSRF checks. To do this, pass in the - ``enforce_csrf_checks`` argument when you construct your - client:: - - >>> from django.test import Client - >>> csrf_client = Client(enforce_csrf_checks=True) - -Making requests -~~~~~~~~~~~~~~~ - -Use the ``django.test.Client`` class to make requests. - -.. class:: Client(enforce_csrf_checks=False, **defaults) - - It requires no arguments at time of construction. However, you can use - keywords arguments to specify some default headers. For example, this will - send a ``User-Agent`` HTTP header in each request:: - - >>> c = Client(HTTP_USER_AGENT='Mozilla/5.0') - - The values from the ``extra`` keywords arguments passed to - :meth:`~django.test.Client.get()`, - :meth:`~django.test.Client.post()`, etc. have precedence over - the defaults passed to the class constructor. - - The ``enforce_csrf_checks`` argument can be used to test CSRF - protection (see above). - - Once you have a ``Client`` instance, you can call any of the following - methods: - - .. method:: Client.get(path, data={}, follow=False, secure=False, **extra) - - .. versionadded:: 1.7 - - The ``secure`` argument was added. - - 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 - - The ``extra`` keyword arguments parameter can be used to specify - headers to be sent in the request. For example:: - - >>> c = Client() - >>> c.get('/customers/details/', {'name': 'fred', 'age': 7}, - ... HTTP_X_REQUESTED_WITH='XMLHttpRequest') - - ...will send the HTTP header ``HTTP_X_REQUESTED_WITH`` to the - details view, which is a good way to test code paths that use the - :meth:`django.http.HttpRequest.is_ajax()` method. - - .. admonition:: CGI specification - - The headers sent via ``**extra`` should follow CGI_ specification. - For example, emulating a different "Host" header as sent in the - HTTP request from the browser to the server should be passed - as ``HTTP_HOST``. - - .. _CGI: http://www.w3.org/CGI/ - - If you already have the GET arguments in URL-encoded form, you can - use that encoding instead of using the data argument. For example, - the previous GET request could also be posed as:: - - >>> c = Client() - >>> c.get('/customers/details/?name=fred&age=7') - - If you provide a URL with both an encoded GET data and a data argument, - the data argument will take precedence. - - If you set ``follow`` to ``True`` the client will follow any redirects - and a ``redirect_chain`` attribute will be set in the response object - containing tuples of the intermediate urls and status codes. - - If you had a URL ``/redirect_me/`` that redirected to ``/next/``, that - redirected to ``/final/``, this is what you'd see:: - - >>> response = c.get('/redirect_me/', follow=True) - >>> response.redirect_chain - [(u'http://testserver/next/', 302), (u'http://testserver/final/', 302)] - - If you set ``secure`` to ``True`` the client will emulate an HTTPS - request. - - .. method:: Client.post(path, data={}, content_type=MULTIPART_CONTENT, follow=False, secure=False, **extra) - - 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. :mimetype:`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 - :mimetype:`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 ``', - '') - - ``html1`` and ``html2`` must be valid HTML. An ``AssertionError`` will be - raised if one of them cannot be parsed. - - Output in case of error can be customized with the ``msg`` argument. - -.. method:: SimpleTestCase.assertHTMLNotEqual(html1, html2, msg=None) - - Asserts that the strings ``html1`` and ``html2`` are *not* equal. The - comparison is based on HTML semantics. See - :meth:`~SimpleTestCase.assertHTMLEqual` for details. - - ``html1`` and ``html2`` must be valid HTML. An ``AssertionError`` will be - raised if one of them cannot be parsed. - - Output in case of error can be customized with the ``msg`` argument. - -.. method:: SimpleTestCase.assertXMLEqual(xml1, xml2, msg=None) - - Asserts that the strings ``xml1`` and ``xml2`` are equal. The - comparison is based on XML semantics. Similarly to - :meth:`~SimpleTestCase.assertHTMLEqual`, the comparison is - made on parsed content, hence only semantic differences are considered, not - syntax differences. When unvalid XML is passed in any parameter, an - ``AssertionError`` is always raised, even if both string are identical. - - Output in case of error can be customized with the ``msg`` argument. - -.. method:: SimpleTestCase.assertXMLNotEqual(xml1, xml2, msg=None) - - Asserts that the strings ``xml1`` and ``xml2`` are *not* equal. The - comparison is based on XML semantics. See - :meth:`~SimpleTestCase.assertXMLEqual` for details. - - Output in case of error can be customized with the ``msg`` argument. - -.. method:: SimpleTestCase.assertInHTML(needle, haystack, count=None, msg_prefix='') - - Asserts that the HTML fragment ``needle`` is contained in the ``haystack`` one. - - If the ``count`` integer argument is specified, then additionally the number - of ``needle`` occurrences will be strictly verified. - - Whitespace in most cases is ignored, and attribute ordering is not - significant. The passed-in arguments must be valid HTML. - -.. method:: SimpleTestCase.assertJSONEqual(raw, expected_data, msg=None) - - Asserts that the JSON fragments ``raw`` and ``expected_data`` are equal. - Usual JSON non-significant whitespace rules apply as the heavyweight is - delegated to the :mod:`json` library. - - Output in case of error can be customized with the ``msg`` argument. - -.. method:: TransactionTestCase.assertQuerysetEqual(qs, values, transform=repr, ordered=True) - - Asserts that a queryset ``qs`` returns a particular list of values ``values``. - - The comparison of the contents of ``qs`` and ``values`` is performed using - the function ``transform``; by default, this means that the ``repr()`` of - each value is compared. Any other callable can be used if ``repr()`` doesn't - provide a unique or helpful comparison. - - By default, the comparison is also ordering dependent. If ``qs`` doesn't - provide an implicit ordering, you can set the ``ordered`` parameter to - ``False``, which turns the comparison into a Python set comparison. - - .. versionchanged:: 1.6 - - The method now checks for undefined order and raises ``ValueError`` - if undefined order is spotted. The ordering is seen as undefined if - the given ``qs`` isn't ordered and the comparison is against more - than one ordered values. - -.. method:: TransactionTestCase.assertNumQueries(num, func, *args, **kwargs) - - Asserts that when ``func`` is called with ``*args`` and ``**kwargs`` that - ``num`` database queries are executed. - - If a ``"using"`` key is present in ``kwargs`` it is used as the database - alias for which to check the number of queries. If you wish to call a - function with a ``using`` parameter you can do it by wrapping the call with - a ``lambda`` to add an extra parameter:: - - self.assertNumQueries(7, lambda: my_function(using=7)) - - You can also use this as a context manager:: - - with self.assertNumQueries(2): - Person.objects.create(name="Aaron") - Person.objects.create(name="Daniel") - -.. _topics-testing-email: - -Email services --------------- - -If any of your Django views send email using :doc:`Django's email -functionality `, you probably don't want to send email each time -you run a test using that view. For this reason, Django's test runner -automatically redirects all Django-sent email to a dummy outbox. This lets you -test every aspect of sending email -- 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 -email backend with a testing backend. -(Don't worry -- this has no effect on any other email senders outside of -Django, such as your machine's mail server, if you're running one.) - -.. currentmodule:: django.core.mail - -.. data:: django.core.mail.outbox - -During test running, each outgoing email is saved in -``django.core.mail.outbox``. This is a simple list of all -:class:`~django.core.mail.EmailMessage` instances that have been sent. -The ``outbox`` attribute is a special attribute that is created *only* when -the ``locmem`` email backend is used. It doesn't normally exist as part of the -:mod:`django.core.mail` module and you can't import it directly. The code -below shows how to access this attribute correctly. - -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 `, 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 = [] - -.. _skipping-tests: - -Skipping tests --------------- - -.. currentmodule:: django.test - -The unittest library provides the :func:`@skipIf ` and -:func:`@skipUnless ` decorators to allow you to skip tests -if you know ahead of time that those tests are going to fail under certain -conditions. - -For example, if your test requires a particular optional library in order to -succeed, you could decorate the test case with :func:`@skipIf -`. Then, the test runner will report that the test wasn't -executed and why, instead of failing the test or omitting the test altogether. - -To supplement these test skipping behaviors, Django provides two -additional skip decorators. Instead of testing a generic boolean, -these decorators check the capabilities of the database, and skip the -test if the database doesn't support a specific named feature. - -The decorators use a string identifier to describe database features. -This string corresponds to attributes of the database connection -features class. See ``django.db.backends.BaseDatabaseFeatures`` -class for a full list of database features that can be used as a basis -for skipping tests. - -.. function:: skipIfDBFeature(feature_name_string) - -Skip the decorated test or ``TestCase`` if the named database feature is -supported. - -For example, the following test will not be executed if the database -supports transactions (e.g., it would *not* run under PostgreSQL, but -it would under MySQL with MyISAM tables):: - - class MyTests(TestCase): - @skipIfDBFeature('supports_transactions') - def test_transaction_behavior(self): - # ... conditional test code - -.. versionchanged:: 1.7 - - ``skipIfDBFeature`` can now be used to decorate a ``TestCase`` class. - -.. function:: skipUnlessDBFeature(feature_name_string) - -Skip the decorated test or ``TestCase`` if the named database feature is *not* -supported. - -For example, the following test will only be executed if the database -supports transactions (e.g., it would run under PostgreSQL, but *not* -under MySQL with MyISAM tables):: - - class MyTests(TestCase): - @skipUnlessDBFeature('supports_transactions') - def test_transaction_behavior(self): - # ... conditional test code - -.. versionchanged:: 1.7 - - ``skipUnlessDBFeature`` can now be used to decorate a ``TestCase`` class. diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt new file mode 100644 index 0000000000..d97644fb72 --- /dev/null +++ b/docs/topics/testing/tools.txt @@ -0,0 +1,1596 @@ +============= +Testing tools +============= + +.. currentmodule:: django.test + +Django provides a small set of tools that come in handy when writing tests. + +.. _test-client: + +The 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. + +* See the chain of redirects (if any) and check the URL and status code at + each step. + +* 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 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 template is being + rendered and that the template is passed the correct context data. + +* Use in-browser frameworks like Selenium_ to test *rendered* HTML and the + *behavior* of Web pages, namely JavaScript functionality. Django also + provides special support for those frameworks; see the section on + :class:`~django.test.LiveServerTestCase` for more details. + +A comprehensive test suite should use a combination of both test types. + +Overview and a quick example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the test client, instantiate ``django.test.Client`` and retrieve +Web pages:: + + >>> from django.test import Client + >>> c = Client() + >>> response = c.post('/login/', {'username': 'john', 'password': 'smith'}) + >>> response.status_code + 200 + >>> response = c.get('/customer/details/') + >>> response.content + '>> 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 :mod:`urllib` or + :mod:`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. + +* By default, the test client will disable any CSRF checks + performed by your site. + + If, for some reason, you *want* the test client to perform CSRF + checks, you can create an instance of the test client that + enforces CSRF checks. To do this, pass in the + ``enforce_csrf_checks`` argument when you construct your + client:: + + >>> from django.test import Client + >>> csrf_client = Client(enforce_csrf_checks=True) + +Making requests +~~~~~~~~~~~~~~~ + +Use the ``django.test.Client`` class to make requests. + +.. class:: Client(enforce_csrf_checks=False, **defaults) + + It requires no arguments at time of construction. However, you can use + keywords arguments to specify some default headers. For example, this will + send a ``User-Agent`` HTTP header in each request:: + + >>> c = Client(HTTP_USER_AGENT='Mozilla/5.0') + + The values from the ``extra`` keywords arguments passed to + :meth:`~django.test.Client.get()`, + :meth:`~django.test.Client.post()`, etc. have precedence over + the defaults passed to the class constructor. + + The ``enforce_csrf_checks`` argument can be used to test CSRF + protection (see above). + + Once you have a ``Client`` instance, you can call any of the following + methods: + + .. method:: Client.get(path, data={}, follow=False, secure=False, **extra) + + .. versionadded:: 1.7 + + The ``secure`` argument was added. + + 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 + + The ``extra`` keyword arguments parameter can be used to specify + headers to be sent in the request. For example:: + + >>> c = Client() + >>> c.get('/customers/details/', {'name': 'fred', 'age': 7}, + ... HTTP_X_REQUESTED_WITH='XMLHttpRequest') + + ...will send the HTTP header ``HTTP_X_REQUESTED_WITH`` to the + details view, which is a good way to test code paths that use the + :meth:`django.http.HttpRequest.is_ajax()` method. + + .. admonition:: CGI specification + + The headers sent via ``**extra`` should follow CGI_ specification. + For example, emulating a different "Host" header as sent in the + HTTP request from the browser to the server should be passed + as ``HTTP_HOST``. + + .. _CGI: http://www.w3.org/CGI/ + + If you already have the GET arguments in URL-encoded form, you can + use that encoding instead of using the data argument. For example, + the previous GET request could also be posed as:: + + >>> c = Client() + >>> c.get('/customers/details/?name=fred&age=7') + + If you provide a URL with both an encoded GET data and a data argument, + the data argument will take precedence. + + If you set ``follow`` to ``True`` the client will follow any redirects + and a ``redirect_chain`` attribute will be set in the response object + containing tuples of the intermediate urls and status codes. + + If you had a URL ``/redirect_me/`` that redirected to ``/next/``, that + redirected to ``/final/``, this is what you'd see:: + + >>> response = c.get('/redirect_me/', follow=True) + >>> response.redirect_chain + [(u'http://testserver/next/', 302), (u'http://testserver/final/', 302)] + + If you set ``secure`` to ``True`` the client will emulate an HTTPS + request. + + .. method:: Client.post(path, data={}, content_type=MULTIPART_CONTENT, follow=False, secure=False, **extra) + + 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. :mimetype:`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 + :mimetype:`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 ``', + '') + + ``html1`` and ``html2`` must be valid HTML. An ``AssertionError`` will be + raised if one of them cannot be parsed. + + Output in case of error can be customized with the ``msg`` argument. + +.. method:: SimpleTestCase.assertHTMLNotEqual(html1, html2, msg=None) + + Asserts that the strings ``html1`` and ``html2`` are *not* equal. The + comparison is based on HTML semantics. See + :meth:`~SimpleTestCase.assertHTMLEqual` for details. + + ``html1`` and ``html2`` must be valid HTML. An ``AssertionError`` will be + raised if one of them cannot be parsed. + + Output in case of error can be customized with the ``msg`` argument. + +.. method:: SimpleTestCase.assertXMLEqual(xml1, xml2, msg=None) + + Asserts that the strings ``xml1`` and ``xml2`` are equal. The + comparison is based on XML semantics. Similarly to + :meth:`~SimpleTestCase.assertHTMLEqual`, the comparison is + made on parsed content, hence only semantic differences are considered, not + syntax differences. When unvalid XML is passed in any parameter, an + ``AssertionError`` is always raised, even if both string are identical. + + Output in case of error can be customized with the ``msg`` argument. + +.. method:: SimpleTestCase.assertXMLNotEqual(xml1, xml2, msg=None) + + Asserts that the strings ``xml1`` and ``xml2`` are *not* equal. The + comparison is based on XML semantics. See + :meth:`~SimpleTestCase.assertXMLEqual` for details. + + Output in case of error can be customized with the ``msg`` argument. + +.. method:: SimpleTestCase.assertInHTML(needle, haystack, count=None, msg_prefix='') + + Asserts that the HTML fragment ``needle`` is contained in the ``haystack`` one. + + If the ``count`` integer argument is specified, then additionally the number + of ``needle`` occurrences will be strictly verified. + + Whitespace in most cases is ignored, and attribute ordering is not + significant. The passed-in arguments must be valid HTML. + +.. method:: SimpleTestCase.assertJSONEqual(raw, expected_data, msg=None) + + Asserts that the JSON fragments ``raw`` and ``expected_data`` are equal. + Usual JSON non-significant whitespace rules apply as the heavyweight is + delegated to the :mod:`json` library. + + Output in case of error can be customized with the ``msg`` argument. + +.. method:: TransactionTestCase.assertQuerysetEqual(qs, values, transform=repr, ordered=True) + + Asserts that a queryset ``qs`` returns a particular list of values ``values``. + + The comparison of the contents of ``qs`` and ``values`` is performed using + the function ``transform``; by default, this means that the ``repr()`` of + each value is compared. Any other callable can be used if ``repr()`` doesn't + provide a unique or helpful comparison. + + By default, the comparison is also ordering dependent. If ``qs`` doesn't + provide an implicit ordering, you can set the ``ordered`` parameter to + ``False``, which turns the comparison into a Python set comparison. + + .. versionchanged:: 1.6 + + The method now checks for undefined order and raises ``ValueError`` + if undefined order is spotted. The ordering is seen as undefined if + the given ``qs`` isn't ordered and the comparison is against more + than one ordered values. + +.. method:: TransactionTestCase.assertNumQueries(num, func, *args, **kwargs) + + Asserts that when ``func`` is called with ``*args`` and ``**kwargs`` that + ``num`` database queries are executed. + + If a ``"using"`` key is present in ``kwargs`` it is used as the database + alias for which to check the number of queries. If you wish to call a + function with a ``using`` parameter you can do it by wrapping the call with + a ``lambda`` to add an extra parameter:: + + self.assertNumQueries(7, lambda: my_function(using=7)) + + You can also use this as a context manager:: + + with self.assertNumQueries(2): + Person.objects.create(name="Aaron") + Person.objects.create(name="Daniel") + +.. _topics-testing-email: + +Email services +-------------- + +If any of your Django views send email using :doc:`Django's email +functionality `, you probably don't want to send email each time +you run a test using that view. For this reason, Django's test runner +automatically redirects all Django-sent email to a dummy outbox. This lets you +test every aspect of sending email -- 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 +email backend with a testing backend. +(Don't worry -- this has no effect on any other email senders outside of +Django, such as your machine's mail server, if you're running one.) + +.. currentmodule:: django.core.mail + +.. data:: django.core.mail.outbox + +During test running, each outgoing email is saved in +``django.core.mail.outbox``. This is a simple list of all +:class:`~django.core.mail.EmailMessage` instances that have been sent. +The ``outbox`` attribute is a special attribute that is created *only* when +the ``locmem`` email backend is used. It doesn't normally exist as part of the +:mod:`django.core.mail` module and you can't import it directly. The code +below shows how to access this attribute correctly. + +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 `, 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 = [] + +.. _skipping-tests: + +Skipping tests +-------------- + +.. currentmodule:: django.test + +The unittest library provides the :func:`@skipIf ` and +:func:`@skipUnless ` decorators to allow you to skip tests +if you know ahead of time that those tests are going to fail under certain +conditions. + +For example, if your test requires a particular optional library in order to +succeed, you could decorate the test case with :func:`@skipIf +`. Then, the test runner will report that the test wasn't +executed and why, instead of failing the test or omitting the test altogether. + +To supplement these test skipping behaviors, Django provides two +additional skip decorators. Instead of testing a generic boolean, +these decorators check the capabilities of the database, and skip the +test if the database doesn't support a specific named feature. + +The decorators use a string identifier to describe database features. +This string corresponds to attributes of the database connection +features class. See ``django.db.backends.BaseDatabaseFeatures`` +class for a full list of database features that can be used as a basis +for skipping tests. + +.. function:: skipIfDBFeature(feature_name_string) + +Skip the decorated test or ``TestCase`` if the named database feature is +supported. + +For example, the following test will not be executed if the database +supports transactions (e.g., it would *not* run under PostgreSQL, but +it would under MySQL with MyISAM tables):: + + class MyTests(TestCase): + @skipIfDBFeature('supports_transactions') + def test_transaction_behavior(self): + # ... conditional test code + +.. versionchanged:: 1.7 + + ``skipIfDBFeature`` can now be used to decorate a ``TestCase`` class. + +.. function:: skipUnlessDBFeature(feature_name_string) + +Skip the decorated test or ``TestCase`` if the named database feature is *not* +supported. + +For example, the following test will only be executed if the database +supports transactions (e.g., it would run under PostgreSQL, but *not* +under MySQL with MyISAM tables):: + + class MyTests(TestCase): + @skipUnlessDBFeature('supports_transactions') + def test_transaction_behavior(self): + # ... conditional test code + +.. versionchanged:: 1.7 + + ``skipUnlessDBFeature`` can now be used to decorate a ``TestCase`` class.