mirror of
https://github.com/django/django.git
synced 2025-08-04 02:48:35 +00:00
Fixed #34140 -- Reformatted code blocks in docs with blacken-docs.
This commit is contained in:
parent
6015bab80e
commit
14459f80ee
193 changed files with 5797 additions and 4481 deletions
|
@ -46,16 +46,18 @@ The following is a unit test using the request factory::
|
|||
|
||||
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')
|
||||
username="jacob", email="jacob@…", password="top_secret"
|
||||
)
|
||||
|
||||
def test_details(self):
|
||||
# Create an instance of a GET request.
|
||||
request = self.factory.get('/customer/details')
|
||||
request = self.factory.get("/customer/details")
|
||||
|
||||
# Recall that middleware are not supported. You can simulate a
|
||||
# logged-in user by setting request.user manually.
|
||||
|
@ -107,10 +109,10 @@ For example, assuming the following class-based view:
|
|||
|
||||
|
||||
class HomeView(TemplateView):
|
||||
template_name = 'myapp/home.html'
|
||||
template_name = "myapp/home.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['environment'] = 'Production'
|
||||
kwargs["environment"] = "Production"
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
You may directly test the ``get_context_data()`` method by first instantiating
|
||||
|
@ -126,12 +128,12 @@ your test's code:
|
|||
|
||||
class HomePageTest(TestCase):
|
||||
def test_environment_set_in_context(self):
|
||||
request = RequestFactory().get('/')
|
||||
request = RequestFactory().get("/")
|
||||
view = HomeView()
|
||||
view.setup(request)
|
||||
|
||||
context = view.get_context_data()
|
||||
self.assertIn('environment', context)
|
||||
self.assertIn("environment", context)
|
||||
|
||||
.. _topics-testing-advanced-multiple-hosts:
|
||||
|
||||
|
@ -150,6 +152,7 @@ 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(
|
||||
|
@ -160,11 +163,7 @@ example, the test suite for docs.djangoproject.com includes the following::
|
|||
|
||||
and the settings file includes a list of the domains supported by the project::
|
||||
|
||||
ALLOWED_HOSTS = [
|
||||
'www.djangoproject.dev',
|
||||
'docs.djangoproject.dev',
|
||||
...
|
||||
]
|
||||
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
|
||||
|
@ -176,10 +175,11 @@ multitenancy). For example, you could write a test for the domain
|
|||
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
|
||||
class MultiDomainTestCase(TestCase):
|
||||
@override_settings(ALLOWED_HOSTS=['otherserver'])
|
||||
@override_settings(ALLOWED_HOSTS=["otherserver"])
|
||||
def test_other_domain(self):
|
||||
response = self.client.get('http://otherserver/foo/bar/')
|
||||
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
|
||||
|
@ -207,21 +207,21 @@ 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
|
||||
"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',
|
||||
"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
|
||||
|
@ -261,36 +261,36 @@ can specify the dependencies that exist using the :setting:`DEPENDENCIES
|
|||
example database configuration::
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
"default": {
|
||||
# ... db settings
|
||||
'TEST': {
|
||||
'DEPENDENCIES': ['diamonds'],
|
||||
"TEST": {
|
||||
"DEPENDENCIES": ["diamonds"],
|
||||
},
|
||||
},
|
||||
'diamonds': {
|
||||
"diamonds": {
|
||||
# ... db settings
|
||||
'TEST': {
|
||||
'DEPENDENCIES': [],
|
||||
"TEST": {
|
||||
"DEPENDENCIES": [],
|
||||
},
|
||||
},
|
||||
'clubs': {
|
||||
"clubs": {
|
||||
# ... db settings
|
||||
'TEST': {
|
||||
'DEPENDENCIES': ['diamonds'],
|
||||
"TEST": {
|
||||
"DEPENDENCIES": ["diamonds"],
|
||||
},
|
||||
},
|
||||
'spades': {
|
||||
"spades": {
|
||||
# ... db settings
|
||||
'TEST': {
|
||||
'DEPENDENCIES': ['diamonds', 'hearts'],
|
||||
"TEST": {
|
||||
"DEPENDENCIES": ["diamonds", "hearts"],
|
||||
},
|
||||
},
|
||||
'hearts': {
|
||||
"hearts": {
|
||||
# ... db settings
|
||||
'TEST': {
|
||||
'DEPENDENCIES': ['diamonds', 'clubs'],
|
||||
"TEST": {
|
||||
"DEPENDENCIES": ["diamonds", "clubs"],
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Under this configuration, the ``diamonds`` database will be created first,
|
||||
|
@ -387,18 +387,21 @@ same file that inherit from ``SerializeMixin`` will run sequentially::
|
|||
from django.test import TestCase
|
||||
from django.test.testcases import SerializeMixin
|
||||
|
||||
|
||||
class ImageTestCaseMixin(SerializeMixin):
|
||||
lockfile = __file__
|
||||
|
||||
def setUp(self):
|
||||
self.filename = os.path.join(temp_storage_dir, 'my_file.png')
|
||||
self.filename = os.path.join(temp_storage_dir, "my_file.png")
|
||||
self.file = create_file(self.filename)
|
||||
|
||||
|
||||
class RemoveImageTests(ImageTestCaseMixin, TestCase):
|
||||
def test_remove_image(self):
|
||||
os.remove(self.filename)
|
||||
self.assertFalse(os.path.exists(self.filename))
|
||||
|
||||
|
||||
class ResizeImageTests(ImageTestCaseMixin, TestCase):
|
||||
def test_resize_image(self):
|
||||
resize_image(self.file, (48, 48))
|
||||
|
@ -443,7 +446,7 @@ Let's take a look inside a couple of those files:
|
|||
from django.test.utils import get_runner
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.test_settings'
|
||||
os.environ["DJANGO_SETTINGS_MODULE"] = "tests.test_settings"
|
||||
django.setup()
|
||||
TestRunner = get_runner(settings)
|
||||
test_runner = TestRunner()
|
||||
|
@ -462,7 +465,7 @@ labels to run, etc.
|
|||
.. code-block:: python
|
||||
:caption: ``tests/test_settings.py``
|
||||
|
||||
SECRET_KEY = 'fake-key'
|
||||
SECRET_KEY = "fake-key"
|
||||
INSTALLED_APPS = [
|
||||
"tests",
|
||||
]
|
||||
|
|
|
@ -27,6 +27,7 @@ transaction to provide isolation::
|
|||
from django.test import TestCase
|
||||
from myapp.models import Animal
|
||||
|
||||
|
||||
class AnimalTestCase(TestCase):
|
||||
def setUp(self):
|
||||
Animal.objects.create(name="lion", sound="roar")
|
||||
|
@ -364,7 +365,7 @@ many users in your tests, you may want to use a custom settings file and set
|
|||
the :setting:`PASSWORD_HASHERS` setting to a faster hashing algorithm::
|
||||
|
||||
PASSWORD_HASHERS = [
|
||||
'django.contrib.auth.hashers.MD5PasswordHasher',
|
||||
"django.contrib.auth.hashers.MD5PasswordHasher",
|
||||
]
|
||||
|
||||
Don't forget to also include in :setting:`PASSWORD_HASHERS` any hashing
|
||||
|
|
|
@ -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):
|
||||
|
||||
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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue