Django uses the unittest module’s built-in test discovery, which will discover tests under the current working directory in any file named with the pattern test*.py.
We recommend that you create a module for your test code, and have separate files for models, views, forms, and any other types of code you need to test. For example:
setUp() is called before each method, setUpClass() and setUpTestData() method are called once for the class.
setUp() and setUpClass() are Python‘s built-in methods in unittest.TestCase, setUpTestData() is a Django defined method in django.test.testcases.TestCase.
Differences:
setUp() is called before every test function to set up any objects that may be modified by the test (every test function will get a "fresh" version of these objects).
setUpClass() is used to perform some class-wide initialization (e.g. overriding settings, creating [database] connections, loading webdrivers).
setUpTestData() is called once at the beginning of the test run for class-level setup. You'd use this to create objects that aren't going to be modified or changed in any of the test methods.
Method called to prepare the test fixture. This is called immediately before calling the test method.
setUp will be called before each test run, and should be used to prepare test dataset for each test run.
For example:
class YourTestClass(TestCase):
def setUp(self):
#Setup run before every test method.
pass
def tearDown(self):
#Clean up run after every test method.
pass
def test_something_that_will_pass(self):
self.assertFalse(False)
def test_something_that_will_fail(self):
self.assertTrue(False)
A class method called before tests in an individual class are run.
SimpleTestCase and its subclasses (e.g. TestCase, …) rely on setUpClass() and tearDownClass() to perform some class-wide initialization (e.g. overriding settings). If you need to override those methods, don’t forget to call the super implementation:
setUpClass is used to perform class-wide initialization/configuration (e.g. creating connections, loading webdrivers). When using setUpClass for instance to open database connection/session you can use tearDownClass to close them.
The class-level atomic block described above allows the creation of initial data at the class level, once for the whole TestCase. This technique allows for faster tests as compared to using setUp().
Note that if the tests are run on a database with no transaction support (for instance, MySQL with the MyISAM engine), setUpTestData() will be called before each test, negating the speed benefits.
django.test.testcases.TestCase Source code:
# django/test/testcases
class TestCase(TransactionTestCase):
...
@classmethod
def setUpTestData(cls):
"""Load initial data for the TestCase."""
pass
For example:
from django.test import TestCase
class MyTests(TestCase):
@classmethod
def setUpTestData(cls):
# Set up data for the whole TestCase
cls.foo = Foo.objects.create(bar="Test")
...
def test1(self):
# Some test using self.foo
...
def test2(self):
# Some other test using self.foo
...
This will discover all files named with the pattern test*.py under the current directory
and run all tests defined using appropriate base classes:
$ python manage.py test
3.2 Running specific tests
If you want to run a subset of your tests you can do so by specifying the full dot path to the package(s), module, TestCase subclass or method:
# Run the specified module
$ python3 manage.py test app_name.tests
# Run the specified module
$ python manage.py test app_name.tests.test_models
# Run the specified class
$ python manage.py test app_name.tests.test_models.ClassModelTest
# Run the specified method
$ python manage.py test app_name.tests.test_models.ClassModelTest.test_get_absolute_url
3.3 Showing more test information
If you want to get more information about the test run you can change the verbosity. For example, to list the test successes as well as failures (and a whole bunch of information about how the testing database is set up) you can set the verbosity to "2" as shown:
$ python manage.py test --verbosity 2
The allowed verbosity levels are 0, 1, 2, and 3, with the default being "1".
For example: testTestA will be loaded first than testTestB.
class Test(TestCase):
def setUp(self):
...
def testTestB(self):
# test code
def testTestA(self):
# test code
A tenet of unit-testing is that each test should be independent of all others. If in your case the code in testTestA must come before testTestB, then you could combine both into one test:
def testTestA_and_TestB(self):
# test code from testTestA
...
# test code from testTestB
or, perhaps better would be
def TestA(self):
# test code
def TestB(self):
# test code
def test_A_then_B(self):
self.TestA()
self.TestB()
The Test class only tests those methods who name begins with a lower-case test.... So you can put in extra helper methods TestA and TestB which won't get run unless you explicitly call them.
# /catalog/tests/test_models.py
from django.test import TestCase
from catalog.models import Author
class AuthorModelTest(TestCase):
@classmethod
def setUpTestData(cls):
# Set up non-modified objects used by all test methods
Author.objects.create(first_name='Big', last_name='Bob')
def test_first_name_label(self):
author = Author.objects.get(id=1)
field_label = author._meta.get_field('first_name').verbose_name
self.assertEqual(field_label, 'first name')
def test_first_name_content(self):
author = Author.objects.get(id=1)
expected_object_name = f'{author.first_name}'
self.assertEqual(expected_object_name, 'Big')
def test_object_name_is_last_name_comma_first_name(self):
author = Author.objects.get(id=1)
expected_object_name = f'{author.last_name}, {author.first_name}'
self.assertEqual(str(author), expected_object_name)
def test_get_absolute_url(self):
author = Author.objects.get(id=1)
# This will also fail if the urlconf is not defined.
self.assertEqual(author.get_absolute_url(), '/catalog/author/1')
5.2 Views tests example
# /catalog/views.py
class AuthorListView(generic.ListView):
"""
Let's start with one of our simplest views, which provides a list of all Authors.
This is displayed at URL '/catalog/authors/' (an URL named 'authors' in the URL configuration). """
model = Author
paginate_by = 10
# /catalog/tests/test_views.py
from django.test import TestCase
from django.urls import reverse
from catalog.models import Author
class AuthorListViewTest(TestCase):
@classmethod
def setUpTestData(cls):
# Create 13 authors for pagination tests
number_of_authors = 13
for author_id in range(number_of_authors):
Author.objects.create(
first_name=f'Christian {author_id}',
last_name=f'Surname {author_id}',
)
def test_view_url_exists_at_desired_location(self):
response = self.client.get('/catalog/authors/')
self.assertEqual(response.status_code, 200)
def test_view_url_accessible_by_name(self):
response = self.client.get(reverse('authors'))
self.assertEqual(response.status_code, 200)
# Arguably if you trust Django then the only thing you need to test is
# that the view is accessible at the correct URL and can be accessed using its name.
# However if you're using a test-driven development process you'll start by writing tests
# that confirm that the view displays all Authors, paginating them in lots of 10.
def test_view_uses_correct_template(self):
response = self.client.get(reverse('authors'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'catalog/author_list.html')
def test_pagination_is_ten(self):
response = self.client.get(reverse('authors'))
self.assertEqual(response.status_code, 200)
self.assertTrue('is_paginated' in response.context)
self.assertTrue(response.context['is_paginated'] == True)
self.assertEqual(len(response.context['author_list']), 10)
def test_lists_all_authors(self):
# Get second page and confirm it has (exactly) remaining 3 items
response = self.client.get(reverse('authors')+'?page=2')
self.assertEqual(response.status_code, 200)
self.assertTrue('is_paginated' in response.context)
self.assertTrue(response.context['is_paginated'] == True)
self.assertEqual(len(response.context['author_list']), 3)
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from myproject.apps.core.models import Account
class AccountTests(APITestCase):
def test_create_account(self):
""" Ensure we can create a new account object. """
url = reverse('account-list')
data = {'name': 'DabApps'}
# The self.client attribute will be an APIClient (instead of Django's default Client) instance.
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Account.objects.count(), 1)
self.assertEqual(Account.objects.get().name, 'DabApps')
def test_retrieve_account(self):
""" Checking the response data """
response = self.client.get('/users/4/')
self.assertEqual(response.data, {'id': 4, 'username': 'lauren'})
Table of Contents:
References:
1. Test structure overview
Django uses the unittest module’s built-in test discovery, which will discover tests under the current working directory in any file named with the pattern test*.py. We recommend that you create a module for your test code, and have separate files for models, views, forms, and any other types of code you need to test. For example:
2. setUp vs setUpClass vs setUpTestData
Similarities:
unittest.TestCase
, setUpTestData() is a Django defined method indjango.test.testcases.TestCase
.Differences:
2.1 setUp
Method called to prepare the test fixture. This is called immediately before calling the test method.
setUp
will be called before each test run, and should be used to prepare test dataset for each test run.For example:
2.2 setUpClass
A class method called before tests in an individual class are run.
SimpleTestCase
and its subclasses (e.g.TestCase
, …) rely onsetUpClass()
andtearDownClass()
to perform some class-wide initialization (e.g. overriding settings). If you need to override those methods, don’t forget to call thesuper
implementation:setUpClass
is used to perform class-wide initialization/configuration (e.g. creating connections, loading webdrivers). When usingsetUpClass
for instance to open database connection/session you can usetearDownClass
to close them.For example:
2.3 setUpTestData
The class-level
atomic
block described above allows the creation of initial data at the class level, once for the wholeTestCase
. This technique allows for faster tests as compared to usingsetUp()
.Note that if the tests are run on a database with no transaction support (for instance, MySQL with the MyISAM engine),
setUpTestData()
will be called before each test, negating the speed benefits.django.test.testcases.TestCase Source code:
For example:
2.4 init method
__init__()
method is not recommended for TestCase class3. How to run the tests
3.1 Running all the tests
3.2 Running specific tests
3.3 Showing more test information
If you want to get more information about the test run you can change the
verbosity
. For example, to list the test successes as well as failures (and a whole bunch of information about how the testing database is set up) you can set the verbosity to "2" as shown:The allowed verbosity levels are 0, 1, 2, and 3, with the default being "1".
4. TestCase testing order
The order to execute is alphabetical.
For example: testTestA will be loaded first than testTestB.
A tenet of unit-testing is that each test should be independent of all others. If in your case the code in testTestA must come before testTestB, then you could combine both into one test:
or, perhaps better would be
The
Test
class only tests those methods who name begins with a lower-casetest...
. So you can put in extra helper methodsTestA
andTestB
which won't get run unless you explicitly call them.5. Testing example
5.1 Models tests example
5.2 Views tests example
5.3 DRF API tests example