Django: TDD and Unit Testing

from coffee import *

Majority of the junior developers I’ve been mentoring are somewhat frightened of Testing in general. As human beings [precisely lazy ones in case of Developers] we want to write code to solve a problem, counter couple of scenarios wrapped in if or try-catch statements and then worry about writing test later on if all! Well in the real-world of development and especially for Test Driven Development things work literally the opposite. In fact you write your tests before writing any code. Yes you’ve read it correctly, Write the tests before anything else!

I hear you asking the same question which I’ve asked five years ago. “How can you write tests, to test your code if you don’t have any code in place”. Well let us see how in the next section.

Lets assume that we’ve just started work on a clients project. The pages they’ve wanted on the main navigation area are homepage, products, about-us and contact us. That means four classes or methods for each page. I’m not saying don’t draw any flowchart diagrams or write pseudo code beforehand. Before you start writing any tests, you should have an idea on how to solve the problem.

So in our case, although its really a brain busting problem, the solution would be us constructing four classes for each Homepage, Products, About Us and Contact Us which inherits from TemplateView so any GET request should return the respective template.

So as a developer I know beforehand on how to solve the problem. Tests are there to test your logical implemantion not to solve your problems.

I highly suggest you read testing tools and unit testing tutorials from Django.

If we’re testing for responses of the browser we will be using the Client class. If we want access and manipulate the request object we use the RequestFactory class.

As we’re testing if we can navigate to different webpages within our site, we will be using Client.

from django.test import TestCase, Client
class TestMainNavigation(TestCase):
   def setUp(self):
self.client = Client()
    def test_homepage(self):
homepage = client.get('homepage/')
self.assertTrue(homepage.response, 200)
self.assertTemplateUsed(homepage, 'homepage.html')
    def test_products(self):
products = client.get('products/')
self.assertTrue(products.response, 200)
self.assertTemplateUsed(products, 'products.html')
    def test_about_us(self):
about_us = client.get('about-us/')
self.assertTrue(about_us.response, 200)
self.assertTemplateUsed(about_us, 'about_us.html')
    def test_contact_us(self):
contact_us = client.get('contact_us/')
self.assertTrue(contact_us.response, 200)
self.assertTemplateUsed(contact_us, 'contact_us.html')

As you can see we’ve inhereted from the TestCase class. The setup method is an initialisation method so feel free to declare variables used throughout your test case. Client class can also make post requests. There are also various Django related assertions. Now if we were to run the test case.

django-admin test TestMainNavigation

We should see the failures

Ran 4 test in 0.009s

FAILED (failures=4)

Well this is completely normal and as expected because we haven’t developed our view to respond yet :)

class Homepage(TemplateView):
template_name = 'homepage.html'
class AboutUs(TemplateView):
template_name = 'about_us.html'
class ContactUs(TemplateView):
template_name = 'contact_us.html'
class Products(TemplateView):
template_name = 'products_us.html'

Now run the tests again and check for the output.

*Hint: Should be a success :)

So although our example was extrodinarily simple, image a scenario where a developer accidentally renamed ‘contact_us.html’ to ‘contact.html’. This will only be flagged when a user tries to access contact_us page. The user will be prompted with something like this, if debug is turned on which is a security risk and should be avoided in production. So your users will hit a 404 without knowing whats going on.

From Google images.

So if you’ve got an ideal CI in place and only deploy after all your tests pass then simple issue like this will never be faced view.

This has been a relatively long article therefore I will write about RequestFactory in my next article. Lets keep it short and simple!