Nov 23, 2021
Django ·9 min read
Writing tests for Django applications
Is your application running as expected or are you on the hunt for relevant solutions to test your application’s output? This might help!
Brief: With the help of the test execution framework and miscellaneous utilities of Django, you can simulate requests, insert test data, analyze your application’s output, and lastly double check your code.
Let’s get started from scratch!
Why is Testing needed at all?
When you write new code, you are very likely to encounter errors - be it expected or not. Testing a system that you have coded makes it reliable and effective which consequently improves the quality of your code. The more the test cases are designed and implemented for a system, the higher is the quality. However, testing a web application can be demanding at times as it is made of a number of layers ranging from HTTP level request handling to validating and processing forms and rendering templates.
Using Django’s test execution framework and assorted services, you can simulate requests, insert test data for the input of test cases, analyze and understand your application’s output and then you can modify your code accordingly. But, how can we test an application? There are many ways to test software like Unit testing, Integration testing, Regression testing, Smoke testing, Alpha testing, Beta testing, and so on.
Testing tools for a Django application
Django offers a hierarchical test framework called the unittest
library. Regardless of its name, it can also be used for integration testing. The framework adds API and tools for testing web and Django-specific behavior of an application. Django also offers an API called LiveServerTestCase
and tools for using various frameworks for testing.
How to write a test in Django
To write a test, we can import any test base class from the django.test
module. There are various test base classes like SimpleTestCase, TransactionTestCase, TestCase, LiveServerTestCase, etc. Then, you can write specific methods by using assert(firstArg, secondArg)
assertion methods, explained below, to check whether your application functions execute as expected. Let’s go through the following very simple and straightforward example-
from django.test import TestCase
def concate_two_strings(str1, str2):
return str1 + str2
class TestExample(TestCase):
def test_concate_two_strings(self):
self.assertEqual(concate_two_strings("Hello, ", "How are you?"), "Hello, How are you?")
In the above code,
TestCase
base class for testing. You can use any base class as per your utility.concate_two_strings
which is our target method for testing.test_concate_two_strings
. The testing methods use special assertion methods that return either True or False that indicates if the test has passed or failed respectively.The above code will pass the test for checking the concatenation of two strings because the result of the target method is as expected.
Types of Testing in Django
There is mainly two types of testing in Django:
In this article, we are going to focus and explore more about Unit testing.
Assertion
Assertions are the essential methods that determine whether a test has passed or failed by returning True or False. You can use multiple assertions in one test.
Let’s see this example,
from django.tests import TestCase
def get_max(num1, num2):
return num1 if num1>=num2 else num2
class TestExample(TestCase):
def test_get_max(self):
self.assertEqual(get_max(4,7),7)
We have taken a very straightforward method get_max
that returns the maximum number from the two numbers num1 and num2. The test_get_max
method above in the TestExample
class checks if the output of the given method matches the expected output. Here, the assertEqual
method will return True because the get_max
method will return the expected number that is 7 (second parameter of the assertEqual method).
Code References
Folder Structure
In this article, we will refer to the application 'ecommerce' and its modules and files for illustrations. This is the ideal structure to follow for your Django application.
└── ecommerce/
├── ecommerce/
│ ├── wsgi.py
│ ├── settings.py
│ ├── urls.py
│ ├── model.py
│ └── tests/
│ ├── test_urls.py
│ └── test_model.py
├── store/
│ ├── admin.py
│ ├── views.py
│ ├── urls.py
│ ├── model.py
│ └── tests/
│ ├── test_views.py
│ ├── test_model.py
│ └── test_urls.py
├── customer/
│ ├── admin.py
│ ├── views.py
│ ├── urls.py
│ ├── model.py
│ └── tests/
│ ├── test_views.py
│ ├── test_model.py
│ └── test_urls.py
└── manage.py
It is a good practice to create a folder called 'Tests' in your app folders and then name your test files with test_{name}.py format.
Project URLs
from django.contrib import admin
from django.urls import path,include
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
path('',include('store.urls'))
]
urlpatterns+=static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
App URLs
from django.urls import path
from . import views
urlpatterns=[
path('',views.store,name='store'),
path('cart/',views.cart,name='cart'),
path('checkout/',views.checkout,name='checkout'),
path('update_item/',views.update_item,name='update_item'),
path('process_order/',views.processOrder,name='process_order'),
]
Unit Testing
Unit testing in Django focuses on each method individually. It uses a standard python library module named unittest
that determines tests using a class-based approach. There are several subtests of Unit testing such as URL testing, Views testing, Model testing, Forms testing, API testing, etc. In Django, we can utilize these tests independently.
URL Testing
There are two ways we can test URLs in our application
The test client is a class of Python that behaves as a dummy user or web browser, allowing you to test your URLs and views and interact with your Django application functionalities.
from django.test import TestCase
class UrlTest(TestCase):
def testHomePage(self):
response = self.client.get('/')
print(response)
self.assertEqual(response.status_code, 200)
In the above code, we defined the test method testHomePage
, in which we got the HTTP response object by hitting the GET '/' URL. If the page would be retrieved, then the response object will contain the body of the page and its metadata like title, status code, etc. By executing the assertEqual
method, we are simply checking if the status code of the response is 200 or not, meaning if the page/URL that we had hit with the client object was found or not. If it wasn’t found, the status code would be 404 and the test method would return False.
from django.test import TestCase
from django.urls import reverse, resolve
from store.views import cart
class UrlTest(TestCase):
def testCartPage(self):
url = reverse('cart')
print("Resolve : ", resolve(url))
self.assertEquals(resolve(url).func, cart)
In the above code, the assertEquals
method checks if the resolver method matches the cart view. This test will pass because the result is as expected.
Now, let’s try the erroneous code.
from django.test import TestCase
from django.urls import reverse, resolve
class UrlTest(TestCase):
def testCartPage(self):
url = reverse('cart')
print("Resolve : ", resolve(url))
self.assertEquals(resolve(url).func, ‘cart’)
The assertEquals
method in the above Testing class will fail because the resolver match returns the cart view and not the string 'cart' which is the second argument of the given assertion method.
Model Testing
Let’s understand the following code
class Product(models.Model):
name = models.CharField(max_length=200,null=True)
price = models.FloatField()
digital = models.BooleanField(default=False,null=True,blank=True)
image = models.ImageField(null=True,blank=True)
def __str__(self):
return self.name
@property
def imageURL(self):
try:
url = self.image.url
except:
url=''
return url
In the above code, we have defined a model called Product
with properties such as name, price, digital, image, and imageURL.
from django.test import TestCase
from store.models import Product
class ModelTest(TestCase):
def testProductModel(self):
product = Product.objects.create(name="ToyCar", price=800)
self.assertEquals(str(product), 'ToyCar')
print("IsInstance : ",isinstance(product,Product))
self.assertTrue(isinstance(product,Product))
Now, we have defined the Testing class named ModelTest
with a method called testProductModel
. In that, a product instance is created with the name 'ToyCar' which we later checked with the assertEquals
method which will return True. Then, we checked if the product instance is the instance of the Product class, which it is so it will return True and the test will pass.
Now, let’s try testing the same code but with a different product name (an error).
from django.test import TestCase
from store.models import Product
class ModelTest(TestCase):
def testProductModel(self):
product = Product.objects.create(name="ToyCar", price=800)
self.assertEquals(str(product), 'ToyCar11')
print("IsInstance : ",isinstance(product,Product))
self.assertTrue(isinstance(product,Product))
In the above code, the assertEquals method will return False as the product name 'ToyCar' does not match with 'ToyCar11' and therefore, the test will fail.
Executing Tests
Once you are done writing the tests for your application, you can run them by executing the following test commands written in the manage.py utility.
python manage.py test
To run the tests specifically within an app module (for 'store' in our case),
python manage.py test store
Run only one test case,
python manage.py test store.tests.test_urls.py
Variants of Assertion available in Django
assertNotEqual
assertNotEqual(firstArg, secondArg, msg=None)
Checks that the firstArg and secondArg are not equal and so returns True otherwise False (meaning test failed).
assertTrue (or assertFalse)
assertTrue(expr, msg=None)
Checks that the bool(expr) is True or False.
Note that this method evaluates bool(expr) instead of expr to be True or False.
assertIsInstance (or assertNotIsInstance)
assertIsInstance(obj, cls, msg=None)
Determines if the object obj is an instance (or not) of the class cls.
assertIs (or assertIsNot)
assertIs(firstObj, secondObj, msg=None)
Tests if the firstObj and secondObj are (or not) the same objects.
assertIn (or assertNotIn)
assertIn(member, container, msg=None)
Verifies if the member is (or not) in the container. It is available in version 3.1
assertIsNone (or assertIsNotNone)
assertIsNone(expr, msg=None)
Checks whether the expr is None or not.
Conclusion
Wrapping it all up, it is good practice to write and run tests in your Django application before deploying it. The approach of testing in Django is straightforward. If you follow all the steps and rules thoroughly then testing can provide you with highly qualitative code with no errors. Furthermore, if you want to dive deeper into the core of Django modules and libraries, its official documentations are super easy to grasp. Have fun coding!
Follow us@ordinarycoders