diff options
| author | django-bot <ops@djangoproject.com> | 2023-02-28 20:53:28 +0100 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2023-03-01 13:03:56 +0100 |
| commit | 14459f80ee3a9e005989db37c26fd13bb6d2fab2 (patch) | |
| tree | eb62429ed696ed3a5389f3a676aecfc6d15a99cc /docs/topics/testing/tools.txt | |
| parent | 6015bab80e28aef2669f6fac53423aa65f70cb08 (diff) | |
Fixed #34140 -- Reformatted code blocks in docs with blacken-docs.
Diffstat (limited to 'docs/topics/testing/tools.txt')
| -rw-r--r-- | docs/topics/testing/tools.txt | 258 |
1 files changed, 142 insertions, 116 deletions
diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt index 2f0e431caa..c4e3c4e5af 100644 --- a/docs/topics/testing/tools.txt +++ b/docs/topics/testing/tools.txt @@ -54,10 +54,10 @@ web pages: >>> from django.test import Client >>> c = Client() - >>> response = c.post('/login/', {'username': 'john', 'password': 'smith'}) + >>> response = c.post("/login/", {"username": "john", "password": "smith"}) >>> response.status_code 200 - >>> response = c.get('/customer/details/') + >>> response = c.get("/customer/details/") >>> response.content b'<!DOCTYPE html...' @@ -76,13 +76,13 @@ Note a few important things about how the test client works: .. code-block:: pycon - >>> c.get('/login/') + >>> c.get("/login/") This is incorrect: .. code-block:: pycon - >>> c.get('https://www.example.com/login/') + >>> c.get("https://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, @@ -173,7 +173,7 @@ Use the ``django.test.Client`` class to make requests. .. code-block:: pycon >>> c = Client() - >>> c.get('/customers/details/', {'name': 'fred', 'age': 7}) + >>> c.get("/customers/details/", {"name": "fred", "age": 7}) ...will result in the evaluation of a GET request equivalent to: @@ -187,8 +187,11 @@ Use the ``django.test.Client`` class to make requests. .. code-block:: pycon >>> c = Client() - >>> c.get('/customers/details/', {'name': 'fred', 'age': 7}, - ... headers={'accept': 'application/json'}) + >>> c.get( + ... "/customers/details/", + ... {"name": "fred", "age": 7}, + ... headers={"accept": "application/json"}, + ... ) ...will send the HTTP header ``HTTP_ACCEPT`` to the details view, which is a good way to test code paths that use the @@ -210,7 +213,7 @@ Use the ``django.test.Client`` class to make requests. .. code-block:: pycon >>> c = Client() - >>> c.get('/customers/details/?name=fred&age=7') + >>> 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. @@ -224,7 +227,7 @@ Use the ``django.test.Client`` class to make requests. .. code-block:: pycon - >>> response = c.get('/redirect_me/', follow=True) + >>> response = c.get("/redirect_me/", follow=True) >>> response.redirect_chain [('http://testserver/next/', 302), ('http://testserver/final/', 302)] @@ -246,7 +249,7 @@ Use the ``django.test.Client`` class to make requests. .. code-block:: pycon >>> c = Client() - >>> c.post('/login/', {'name': 'fred', 'passwd': 'secret'}) + >>> c.post("/login/", {"name": "fred", "passwd": "secret"}) ...will result in the evaluation of a POST request to this URL: @@ -284,7 +287,7 @@ Use the ``django.test.Client`` class to make requests. list or tuple for the required key. For example, this value of ``data`` would submit three selected values for the field named ``choices``:: - {'choices': ['a', 'b', 'd']} + {"choices": ["a", "b", "d"]} Submitting files is a special case. To POST a file, you need only provide the file field name as a key, and a file handle to the file you @@ -295,8 +298,9 @@ Use the ``django.test.Client`` class to make requests. .. code-block:: pycon >>> c = Client() - >>> with open('wishlist.doc', 'rb') as fp: - ... c.post('/customers/wishes/', {'name': 'fred', 'attachment': fp}) + >>> with open("wishlist.doc", "rb") as fp: + ... c.post("/customers/wishes/", {"name": "fred", "attachment": fp}) + ... You may also provide any file-like object (e.g., :class:`~io.StringIO` or :class:`~io.BytesIO`) as a file handle. If you're uploading to an @@ -334,7 +338,7 @@ Use the ``django.test.Client`` class to make requests. .. code-block:: pycon - >>> c.post('/login/?visitor=true', {'name': 'fred', 'passwd': 'secret'}) + >>> c.post("/login/?visitor=true", {"name": "fred", "passwd": "secret"}) ... the view handling this request could interrogate request.POST to retrieve the username and password, and could interrogate request.GET @@ -456,7 +460,7 @@ Use the ``django.test.Client`` class to make requests. .. code-block:: pycon >>> c = Client() - >>> c.login(username='fred', password='secret') + >>> c.login(username="fred", password="secret") # Now you can access a view that's only available to logged-in users. @@ -551,8 +555,8 @@ Specifically, a ``Response`` object has the following attributes: .. code-block:: pycon - >>> response = client.get('/foo/') - >>> response.context['name'] + >>> response = client.get("/foo/") + >>> response.context["name"] 'Arthur' .. admonition:: Not using Django templates? @@ -585,8 +589,8 @@ Specifically, a ``Response`` object has the following attributes: .. code-block:: pycon - >>> response = client.get('/foo/') - >>> response.json()['name'] + >>> response = client.get("/foo/") + >>> response.json()["name"] 'Arthur' If the ``Content-Type`` header is not ``"application/json"``, then a @@ -696,7 +700,7 @@ access these properties as part of a test condition. def test_something(self): session = self.client.session - session['somekey'] = 'test' + session["somekey"] = "test" session.save() Setting the language @@ -712,9 +716,10 @@ a name of :setting:`LANGUAGE_COOKIE_NAME` and a value of the language code:: from django.conf import settings + def test_language_using_cookie(self): - self.client.cookies.load({settings.LANGUAGE_COOKIE_NAME: 'fr'}) - response = self.client.get('/') + self.client.cookies.load({settings.LANGUAGE_COOKIE_NAME: "fr"}) + response = self.client.get("/") self.assertEqual(response.content, b"Bienvenue sur mon site.") or by including the ``Accept-Language`` HTTP header in the request:: @@ -738,9 +743,10 @@ If the middleware isn't enabled, the active language may be set using from django.utils import translation + def test_language_using_override(self): - with translation.override('fr'): - response = self.client.get('/') + with translation.override("fr"): + response = self.client.get("/") self.assertEqual(response.content, b"Bienvenue sur mon site.") More details are in :ref:`explicitly-setting-the-active-language`. @@ -753,6 +759,7 @@ The following is a unit test using the test client:: import unittest from django.test import Client + class SimpleTest(unittest.TestCase): def setUp(self): # Every test needs a client. @@ -760,13 +767,13 @@ The following is a unit test using the test client:: def test_details(self): # Issue a GET request. - response = self.client.get('/customer/details/') + response = self.client.get("/customer/details/") # Check that the response is 200 OK. self.assertEqual(response.status_code, 200) # Check that the rendered context contains 5 customers. - self.assertEqual(len(response.context['customers']), 5) + self.assertEqual(len(response.context["customers"]), 5) .. seealso:: @@ -847,7 +854,6 @@ If your tests make any database queries, use subclasses methods, don't forget to call the ``super`` implementation:: class MyTestCase(TestCase): - @classmethod def setUpClass(cls): super().setUpClass() @@ -947,6 +953,7 @@ It also provides an additional method: from django.test import TestCase + class MyTests(TestCase): @classmethod def setUpTestData(cls): @@ -994,15 +1001,15 @@ It also provides an additional method: def test_post(self): with self.captureOnCommitCallbacks(execute=True) as callbacks: response = self.client.post( - '/contact/', - {'message': 'I like your site'}, + "/contact/", + {"message": "I like your site"}, ) self.assertEqual(response.status_code, 200) self.assertEqual(len(callbacks), 1) self.assertEqual(len(mail.outbox), 1) - self.assertEqual(mail.outbox[0].subject, 'Contact Form') - self.assertEqual(mail.outbox[0].body, 'I like your site') + self.assertEqual(mail.outbox[0].subject, "Contact Form") + self.assertEqual(mail.outbox[0].body, "I like your site") .. _live-test-server: @@ -1047,8 +1054,9 @@ The code for this test may look as follows:: from selenium.webdriver.common.by import By from selenium.webdriver.firefox.webdriver import WebDriver + class MySeleniumTests(StaticLiveServerTestCase): - fixtures = ['user-data.json'] + fixtures = ["user-data.json"] @classmethod def setUpClass(cls): @@ -1062,11 +1070,11 @@ The code for this test may look as follows:: super().tearDownClass() def test_login(self): - self.selenium.get(f'{self.live_server_url}/login/') + self.selenium.get(f"{self.live_server_url}/login/") username_input = self.selenium.find_element(By.NAME, "username") - username_input.send_keys('myuser') + username_input.send_keys("myuser") password_input = self.selenium.find_element(By.NAME, "password") - password_input.send_keys('secret') + password_input.send_keys("secret") self.selenium.find_element(By.XPATH, '//input[@value="Log in"]').click() Finally, you may run the test as follows: @@ -1103,12 +1111,14 @@ out the `full reference`_ for more details. def test_login(self): from selenium.webdriver.support.wait import WebDriverWait + timeout = 2 ... self.selenium.find_element(By.XPATH, '//input[@value="Log in"]').click() # Wait until the response is received WebDriverWait(self.selenium, timeout).until( - lambda driver: driver.find_element(By.TAG_NAME, 'body')) + lambda driver: driver.find_element(By.TAG_NAME, "body") + ) The tricky thing here is that there's really no such thing as a "page load," especially in modern web apps that generate HTML dynamically after the @@ -1138,28 +1148,30 @@ This means, instead of instantiating a ``Client`` in each test:: import unittest from django.test import Client + class SimpleTest(unittest.TestCase): def test_details(self): client = Client() - response = client.get('/customer/details/') + response = client.get("/customer/details/") self.assertEqual(response.status_code, 200) def test_index(self): client = Client() - response = client.get('/customer/index/') + response = client.get("/customer/index/") self.assertEqual(response.status_code, 200) ...you can refer to ``self.client``, like so:: from django.test import TestCase + class SimpleTest(TestCase): def test_details(self): - response = self.client.get('/customer/details/') + response = self.client.get("/customer/details/") self.assertEqual(response.status_code, 200) def test_index(self): - response = self.client.get('/customer/index/') + response = self.client.get("/customer/index/") self.assertEqual(response.status_code, 200) Customizing the test client @@ -1173,10 +1185,12 @@ attribute:: from django.test import Client, TestCase + class MyTestClient(Client): # Specialized methods for your environment ... + class MyTest(TestCase): client_class = MyTestClient @@ -1213,8 +1227,9 @@ subclass:: from django.test import TestCase from myapp.models import Animal + class AnimalTestCase(TestCase): - fixtures = ['mammals.json', 'birds'] + fixtures = ["mammals.json", "birds"] def setUp(self): # Test definitions as before. @@ -1281,7 +1296,7 @@ to be flushed. For example:: class TestMyViews(TransactionTestCase): - databases = {'default', 'other'} + databases = {"default", "other"} def test_index_page_view(self): call_some_test_code() @@ -1309,7 +1324,7 @@ wrapping against non-``default`` databases. For example:: class OtherDBTests(TestCase): - databases = {'other'} + databases = {"other"} def test_other_db_query(self): ... @@ -1339,18 +1354,17 @@ Django provides a standard Python context manager (see :pep:`343`) called from django.test import TestCase - class LoginTestCase(TestCase): + class LoginTestCase(TestCase): def test_login(self): - # First check for the default behavior - response = self.client.get('/sekrit/') - self.assertRedirects(response, '/accounts/login/?next=/sekrit/') + response = self.client.get("/sekrit/") + self.assertRedirects(response, "/accounts/login/?next=/sekrit/") # Then override the LOGIN_URL setting - with self.settings(LOGIN_URL='/other/login/'): - response = self.client.get('/sekrit/') - self.assertRedirects(response, '/other/login/?next=/sekrit/') + with self.settings(LOGIN_URL="/other/login/"): + response = self.client.get("/sekrit/") + self.assertRedirects(response, "/other/login/?next=/sekrit/") This example will override the :setting:`LOGIN_URL` setting for the code in the ``with`` block and reset its value to the previous state afterward. @@ -1364,19 +1378,21 @@ settings changes:: from django.test import TestCase - class MiddlewareTestCase(TestCase): + class MiddlewareTestCase(TestCase): def test_cache_middleware(self): - with self.modify_settings(MIDDLEWARE={ - 'append': 'django.middleware.cache.FetchFromCacheMiddleware', - 'prepend': 'django.middleware.cache.UpdateCacheMiddleware', - 'remove': [ - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - ], - }): - response = self.client.get('/') + with self.modify_settings( + MIDDLEWARE={ + "append": "django.middleware.cache.FetchFromCacheMiddleware", + "prepend": "django.middleware.cache.UpdateCacheMiddleware", + "remove": [ + "django.contrib.sessions.middleware.SessionMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + ], + } + ): + response = self.client.get("/") # ... For each action, you can supply either a list of values or a string. When the @@ -1391,23 +1407,23 @@ like this:: from django.test import TestCase, override_settings - class LoginTestCase(TestCase): - @override_settings(LOGIN_URL='/other/login/') + class LoginTestCase(TestCase): + @override_settings(LOGIN_URL="/other/login/") def test_login(self): - response = self.client.get('/sekrit/') - self.assertRedirects(response, '/other/login/?next=/sekrit/') + response = self.client.get("/sekrit/") + self.assertRedirects(response, "/other/login/?next=/sekrit/") The decorator can also be applied to :class:`~django.test.TestCase` classes:: from django.test import TestCase, override_settings - @override_settings(LOGIN_URL='/other/login/') - class LoginTestCase(TestCase): + @override_settings(LOGIN_URL="/other/login/") + class LoginTestCase(TestCase): def test_login(self): - response = self.client.get('/sekrit/') - self.assertRedirects(response, '/other/login/?next=/sekrit/') + response = self.client.get("/sekrit/") + self.assertRedirects(response, "/other/login/?next=/sekrit/") .. function:: modify_settings(*args, **kwargs) @@ -1416,28 +1432,32 @@ decorator:: from django.test import TestCase, modify_settings - class MiddlewareTestCase(TestCase): - @modify_settings(MIDDLEWARE={ - 'append': 'django.middleware.cache.FetchFromCacheMiddleware', - 'prepend': 'django.middleware.cache.UpdateCacheMiddleware', - }) + class MiddlewareTestCase(TestCase): + @modify_settings( + MIDDLEWARE={ + "append": "django.middleware.cache.FetchFromCacheMiddleware", + "prepend": "django.middleware.cache.UpdateCacheMiddleware", + } + ) def test_cache_middleware(self): - response = self.client.get('/') + response = self.client.get("/") # ... The decorator can also be applied to test case classes:: from django.test import TestCase, modify_settings - @modify_settings(MIDDLEWARE={ - 'append': 'django.middleware.cache.FetchFromCacheMiddleware', - 'prepend': 'django.middleware.cache.UpdateCacheMiddleware', - }) - class MiddlewareTestCase(TestCase): + @modify_settings( + MIDDLEWARE={ + "append": "django.middleware.cache.FetchFromCacheMiddleware", + "prepend": "django.middleware.cache.UpdateCacheMiddleware", + } + ) + class MiddlewareTestCase(TestCase): def test_cache_middleware(self): - response = self.client.get('/') + response = self.client.get("/") # ... .. note:: @@ -1515,19 +1535,22 @@ Isolating apps from django.test import SimpleTestCase from django.test.utils import isolate_apps - class MyModelTests(SimpleTestCase): + class MyModelTests(SimpleTestCase): @isolate_apps("app_label") def test_model_definition(self): class TestModel(models.Model): pass + ... … or:: with isolate_apps("app_label"): + class TestModel(models.Model): pass + ... The decorator form can also be applied to classes. @@ -1548,6 +1571,7 @@ Isolating apps def test_model_definition(self): class TestModel(models.Model): pass + self.assertIs(self.apps.get_model("app_label", "TestModel"), TestModel) … or alternatively as an argument on the test method when used as a method @@ -1558,6 +1582,7 @@ Isolating apps def test_model_definition(self, apps): class TestModel(models.Model): pass + self.assertIs(apps.get_model("app_label", "TestModel"), TestModel) .. _emptying-test-outbox: @@ -1600,8 +1625,8 @@ your test suite. given, returns a context manager so that the code being tested can be written inline rather than as a function:: - with self.assertRaisesMessage(ValueError, 'invalid literal for int()'): - int('a') + with self.assertRaisesMessage(ValueError, "invalid literal for int()"): + int("a") .. method:: SimpleTestCase.assertWarnsMessage(expected_warning, expected_message, callable, *args, **kwargs) SimpleTestCase.assertWarnsMessage(expected_warning, expected_message) @@ -1627,7 +1652,9 @@ your test suite. ``a@a.com`` as a valid email address, but rejects ``aaa`` with a reasonable error message:: - self.assertFieldOutput(EmailField, {'a@a.com': 'a@a.com'}, {'aaa': ['Enter a valid email address.']}) + self.assertFieldOutput( + EmailField, {"a@a.com": "a@a.com"}, {"aaa": ["Enter a valid email address."]} + ) .. method:: SimpleTestCase.assertFormError(form, field, errors, msg_prefix='') @@ -1710,10 +1737,10 @@ your test suite. You can use this as a context manager, like this:: - with self.assertTemplateUsed('index.html'): - render_to_string('index.html') - with self.assertTemplateUsed(template_name='index.html'): - render_to_string('index.html') + with self.assertTemplateUsed("index.html"): + render_to_string("index.html") + with self.assertTemplateUsed(template_name="index.html"): + render_to_string("index.html") .. method:: SimpleTestCase.assertTemplateNotUsed(response, template_name, msg_prefix='') @@ -1771,14 +1798,14 @@ your test suite. ``AssertionError``:: self.assertHTMLEqual( - '<p>Hello <b>'world'!</p>', - '''<p> + "<p>Hello <b>'world'!</p>", + """<p> Hello <b>'world'! </b> - </p>''' + </p>""", ) self.assertHTMLEqual( '<input type="checkbox" checked="checked" id="id_accept_terms" />', - '<input id="id_accept_terms" type="checkbox" checked>' + '<input id="id_accept_terms" type="checkbox" checked>', ) ``html1`` and ``html2`` must contain HTML. An ``AssertionError`` will be @@ -1875,7 +1902,7 @@ your test suite. If a ``"using"`` key is present in ``kwargs`` it is used as the database alias for which to check the number of queries:: - self.assertNumQueries(7, using='non_default_db') + self.assertNumQueries(7, using="non_default_db") 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:: @@ -1898,33 +1925,32 @@ you might label fast or slow tests:: from django.test import tag - class SampleTestCase(TestCase): - @tag('fast') + class SampleTestCase(TestCase): + @tag("fast") def test_fast(self): ... - @tag('slow') + @tag("slow") def test_slow(self): ... - @tag('slow', 'core') + @tag("slow", "core") def test_slow_but_core(self): ... You can also tag a test case:: - @tag('slow', 'core') + @tag("slow", "core") class SampleTestCase(TestCase): ... Subclasses inherit tags from superclasses, and methods inherit tags from their class. Given:: - @tag('foo') + @tag("foo") class SampleTestCaseChild(SampleTestCase): - - @tag('bar') + @tag("bar") def test(self): ... @@ -1988,11 +2014,7 @@ test client, with two exceptions: .. code-block:: pycon >>> c = AsyncClient() - >>> c.get( - ... '/customers/details/', - ... {'name': 'fred', 'age': 7}, - ... ACCEPT='application/json' - ... ) + >>> c.get("/customers/details/", {"name": "fred", "age": 7}, ACCEPT="application/json") .. versionchanged:: 4.2 @@ -2001,7 +2023,7 @@ test client, with two exceptions: Using ``AsyncClient`` any method that makes a request must be awaited:: async def test_my_thing(self): - response = await self.async_client.get('/some-url/') + response = await self.async_client.get("/some-url/") self.assertEqual(response.status_code, 200) The asynchronous client can also call synchronous views; it runs through @@ -2023,8 +2045,8 @@ creates. from asgiref.sync import async_to_sync from django.test import TestCase - class MyTests(TestCase): + class MyTests(TestCase): @mock.patch(...) @async_to_sync async def test_my_thing(self): @@ -2065,12 +2087,15 @@ 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'], + "Subject here", + "Here is the message.", + "from@example.com", + ["to@example.com"], fail_silently=False, ) @@ -2078,7 +2103,7 @@ and contents:: self.assertEqual(len(mail.outbox), 1) # Verify that the subject of the first message is correct. - self.assertEqual(mail.outbox[0].subject, 'Subject here') + self.assertEqual(mail.outbox[0].subject, "Subject here") As noted :ref:`previously <emptying-test-outbox>`, the test outbox is emptied at the start of every test in a Django ``*TestCase``. To empty the outbox @@ -2102,11 +2127,12 @@ redirected into a ``StringIO`` instance:: from django.core.management import call_command from django.test import TestCase + class ClosepollTest(TestCase): def test_command_output(self): out = StringIO() - call_command('closepoll', stdout=out) - self.assertIn('Expected output', out.getvalue()) + call_command("closepoll", stdout=out) + self.assertIn("Expected output", out.getvalue()) .. _skipping-tests: @@ -2147,7 +2173,7 @@ supports transactions (e.g., it would *not* run under PostgreSQL, but it would under MySQL with MyISAM tables):: class MyTests(TestCase): - @skipIfDBFeature('supports_transactions') + @skipIfDBFeature("supports_transactions") def test_transaction_behavior(self): # ... conditional test code pass @@ -2162,7 +2188,7 @@ supports transactions (e.g., it would run under PostgreSQL, but *not* under MySQL with MyISAM tables):: class MyTests(TestCase): - @skipUnlessDBFeature('supports_transactions') + @skipUnlessDBFeature("supports_transactions") def test_transaction_behavior(self): # ... conditional test code pass |
