One of the most popular techniques used for writing programs is Test Driven Development (TDD). This disciplined approach to developing software involves writing the test for a piece of functionality before the actual implementation.
Essentially what you are doing with TDD is writing a test and then writing the simplest code possible to make that test pass. You repeat this for each piece of required functionality until all the tests pass. Once this is done, you can go back and refactor your code to make it even better.
To some, this may seem rather tedious but many programmers swear by this approach to programming because of the benefits it brings.
Benefits of the TDD approach
Here are some of the main reasons why TDD is so popular.
Simplification - Tests are designed to be simple and specific. Each test should be trying to do one thing and one thing only. As such you will be writing code that conforms to these rules. This means that each function is likely to be shorter and therefore simpler. The same applies to classes, which will most likely be smaller and focus on one thing.
Fewer Bugs - Having simplified code leads to fewer bugs. A lot of the time, bugs are caused by complexities in the code, so in this case, you are essentially killing two birds with one stone. Cleaner units of code reduce the risk for errors.
Focus - Writing tests first requires you to really think about what each function needs to do. You will likely have a list of features that need to be implemented and by creating a suite of tests based on that information, you can be sure you have covered everything. Then by focusing on a single function at a time, you can keep productivity high as you are simply solving a series of problems one by one, rather than tackling the entire thing in one go. This stops the project from becoming overwhelming, allowing you to work methodically through the project.
Tests act as documentation - Tests can serve as documentation to a developer. If you’re working on the project and are unsure of how a certain library or class work, the tests can offer useful information and help you understand the code in front of you.
Safe Refactoring - Once a test has passed, you then have a function that can be refactored safely as you will have the test cases to help you out if anything goes wrong.
Cost - Initially, the cost of TDD is higher. However, over time a project that doesn’t have tests will usually end up costing more because it will run into more problems. Not only does TDD save time on fixing bugs but TDD also acts as a safety net, meaning that changes to functionality are less likely to break something.
Although TDD has many positive things going for it, there is one big disadvantage to TDD. Testing can seem like a daunting and time-consuming addition to a set of already complex tasks that developers might choose to avoid. This means it probably isn’t the best idea to delve into TDD if you are working with tight deadlines. Although the positives outweigh the negatives, you should give yourself a comfortable amount of time to get used to TDD.
The TDD Cycle
TDD can be broken down into six steps. These are:
- Write test
- Run test (Should Fail)
- Write code
- Run test (Should Pass)
First of all, you need to write and run the test. It will fail because you haven’t written the functionality it is testing yet. You then write the necessary code to make the test pass. Once you have it working as it should you can refactor it but it doesn’t need to be perfect the first time around. The primary goal is to get all your tests to pass. Whether you choose to refactor all your code at the end or as you go is up to you but considering TDD is designed to help you work through a project piece by piece, it makes sense to refactor as you go. That way you are working from start to finish on one function at a time.
Web Server Setup Using TDD
Now that you have a general understanding of TDD, let’s put it into action and use it to set up a basic application using the popular Python web framework, Django.
To start, you need to get a virtual environment running on your machine. To do this we will be using a tool called
virtualenv. To install
virtualenv run the following command.
$ pip install virtualenv
Now that we have that installed, we can go ahead a create our virtual environment. To do so, run the following command.
$ virtualenv mypython
Then, to activate the virtual environment enter the following.
$ source mypython/bin/activate
You should now see
(mypython) on your terminal line. This tells you the virtual environment is active and you are working within it. To deactivate the virtual environment, simply enter
Before we can continue we need to check Django is actually installed. We will do this by firing up Django’s development server and checking if it gives us a webpage on our local machine. To do this we will use the selenium browser automation tool.
You can install Selenium using the following pip command.
$ pip install -U selenium
Alternatively, you can download the source distribution from PyPi, extract the files and run:
$ python setup.py install
You should now have Selenium installed. You can find more information on Selenium, such as API commands and operations by following this link.
Next, we need to install Django. The easiest way to install this is by using pip and simply entering the following command.
$ pip install django
If you want to install Django using a different method, you can visit the official website which has plenty of information on how to do so. Just follow this link.
Now that everything is installed, we can move onto testing. Create a new Python file called
functional_tests.py and save it wherever you want to keep the code for your project. Then enter the following code.
from selenium import webdriver
browser = webdriver.Firefox()
assert ‘Django’ in browser.title
This is our functional test. This test pops up a Firefox window, uses it to display a webpage from the local PC and checks that the page has the word ‘Django’ in it.
Now that we know what it does, let’s run it with the following command.
$ python functional_tests.py
You should see a browser window pop up and try to open
localhost:8000. It should give you the ‘Unable to connect’ error page. When you switch back to your console you should see an error message telling you Selenium hit an error page.
This is all fine. This is supposed to happen. We have now written and ran our failing test, meaning we can move on to the next step which is getting that test to pass.
Now that we have that out of the way, it’s time to create our project. This will be the main container for our site. Luckily, Django provides a little command-line tool for this. Simply enter this command into your terminal.
$ django-admin.py startproject superlists .
This creates a file called manage.py in your current folder as well as a subfolder called
superlists with more stuff inside it. The
superlists folder is for stuff that applies to the whole project. Make sure to include the ‘.’ at the end of the command. If you see two nested folders called
superlists it will be because you forgot to add the ‘.’ and you will need to delete them and try again.
Your directory should be structured like this.
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
Take note of the
manage.py file. This file is packed with cool features and one of them is being able to run a development server. To do this, we need to run the following command.
$ python manage.py runserver
We now have the development server up and running on our machine. You can quit the server using Ctrl-C. Put that to one side for now and open up another shell. Navigate to your project folder and activate your
virtualenv. Then run the test again.
$ python functional_tests.py
This time around, there should be no
AssertionError and the page you get should look different. It may not look like much but we have successfully got our test from a fail to a pass!
It is worth mentioning that it is good practice to put a
tests.py file in each app directory. An app refers to a submodule of a project. It is self-sufficient and not intertwined with other apps in the project. This means that, in theory, you can drop the app into another project without needing to make any modifications.
A Django project is the whole website. Within the project, you could then have an app for articles, an app for ranking tables and another app for fixtures and results. These could interact with each other through well documented public classes and accessor methods.
functional_tests.py file is in the project root as it is running a test on the project, not an individual app.
Most of the time TDD is just applied to the functions of a program but this example shows that the method can be used in other aspects of programming. Applying this way of working to other parts of your projects will improve not only the quality of your code but also your ability to problem solve.