summaryrefslogtreecommitdiff
path: root/docs/internals/contributing/writing-code/unit-tests.txt
diff options
context:
space:
mode:
Diffstat (limited to 'docs/internals/contributing/writing-code/unit-tests.txt')
-rw-r--r--docs/internals/contributing/writing-code/unit-tests.txt114
1 files changed, 114 insertions, 0 deletions
diff --git a/docs/internals/contributing/writing-code/unit-tests.txt b/docs/internals/contributing/writing-code/unit-tests.txt
index 75747b3110..31e795ee66 100644
--- a/docs/internals/contributing/writing-code/unit-tests.txt
+++ b/docs/internals/contributing/writing-code/unit-tests.txt
@@ -303,3 +303,117 @@ purpose.
Support for running tests in parallel and the ``--parallel`` option were
added.
+
+Tips for writing tests
+----------------------
+
+.. highlight:: python
+
+Isolating model registration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To avoid polluting the global :attr:`~django.apps.apps` registry and prevent
+unnecessary table creation, models defined in a test method should be bound to
+a temporary ``Apps`` instance::
+
+ from django.apps.registry import Apps
+ from django.db import models
+ from django.test import SimpleTestCase
+
+ class TestModelDefinition(SimpleTestCase):
+ def test_model_definition(self):
+ test_apps = Apps(['app_label'])
+
+ class TestModel(models.Model):
+ class Meta:
+ apps = test_apps
+ ...
+
+.. function:: django.test.utils.isolate_apps(*app_labels, attr_name=None, kwarg_name=None)
+
+.. versionadded:: 1.10
+
+Since this pattern involves a lot of boilerplate, Django provides the
+:func:`~django.test.utils.isolate_apps` decorator. It's used like this::
+
+ from django.db import models
+ from django.test import SimpleTestCase
+ from django.test.utils import isolate_apps
+
+ class TestModelDefinition(SimpleTestCase):
+ @isolate_apps('app_label')
+ def test_model_definition(self):
+ class TestModel(models.Model):
+ pass
+ ...
+
+.. admonition:: Setting ``app_label``
+
+ Models defined in a test method with no explicit
+ :attr:`~django.db.models.Options.app_label` are automatically assigned the
+ label of the app in which their test class is located.
+
+ In order to make sure the models defined within the context of
+ :func:`~django.test.utils.isolate_apps` instances are correctly
+ installed, you should pass the set of targeted ``app_label`` as arguments:
+
+ .. snippet::
+ :filename: tests/app_label/tests.py
+
+ from django.db import models
+ from django.test import SimpleTestCase
+ from django.test.utils import isolate_apps
+
+ class TestModelDefinition(SimpleTestCase):
+ @isolate_apps('app_label', 'other_app_label')
+ def test_model_definition(self):
+ # This model automatically receives app_label='app_label'
+ class TestModel(models.Model):
+ pass
+
+ class OtherAppModel(models.Model):
+ class Meta:
+ app_label = 'other_app_label'
+ ...
+
+The decorator can also be applied to classes::
+
+ from django.db import models
+ from django.test import SimpleTestCase
+ from django.test.utils import isolate_apps
+
+ @isolate_apps('app_label')
+ class TestModelDefinition(SimpleTestCase):
+ def test_model_definition(self):
+ class TestModel(models.Model):
+ pass
+ ...
+
+The temporary ``Apps`` instance used to isolate model registration can be
+retrieved as an attribute when used as a class decorator by using the
+``attr_name`` parameter::
+
+ from django.db import models
+ from django.test import SimpleTestCase
+ from django.test.utils import isolate_apps
+
+ @isolate_apps('app_label', attr_name='apps')
+ class TestModelDefinition(SimpleTestCase):
+ def test_model_definition(self):
+ class TestModel(models.Model):
+ pass
+ self.assertIs(self.apps.get_model('app_label', 'TestModel'), TestModel)
+
+Or as an argument on the test method when used as a method decorator by using
+the ``kwarg_name`` parameter::
+
+ from django.db import models
+ from django.test import SimpleTestCase
+ from django.test.utils import isolate_apps
+
+ class TestModelDefinition(SimpleTestCase):
+ @isolate_apps('app_label', kwarg_name='apps')
+ def test_model_definition(self, apps):
+ class TestModel(models.Model):
+ pass
+ self.assertIs(apps.get_model('app_label', 'TestModel'), TestModel)