Learning Test-Driven Development & Why It's Useful

I am a self taught software engineer, with a focus on python, and cloud technologies. My interests include controlling anything mechanical through software. I found my love of software through robotics, and IOT!
Introduction
As a self-taught developer, you may have heard of Test-Driven Development or TDD and wonder what it is. A lot of the tutorials that are out there do not talk about TDD and how you can use it to complete your projects. Often tests are overlooked as an item of completion for self-taught developers because many say TDD is dead and often we want to get directly to the tasks at hand and have a completed project under our belt. However, in production code, tests are very important to make sure that the product delivered to the customer works as intended. Learning Test-Driven Development is a great way to show employers that you're not just a weekend warrior coder, but you deliver thoughout code and add value to the business.
What is Test Driven Development?
Test-Driven Development is the process of writing unit tests prior to writing your code. Developers create small test cases for each feature of the application, then create the code for the features of the application. According to Wikipedia Test-Driven Development
"is a software development process relying on software requirements being converted to test cases before software is fully developed, and tracking all software development by repeatedly testing the software against all test cases." - Wikipedia
What Companies use TDD?
TDD is a methodology derived from Agile and Extreme Programming frameworks. So it is a safe bet to say that any companies that implement these development strategies will use some form of TDD. However, it is often said that most companies do not use the practice of TDD as it is designed. This is because of deadlines, having more code to write, and the possibility of constantly adding and updating tests. Despite this, many claim TDD is still alive and well and has its uses in specific projects, in particular, algorithmic features and stable applications.
How TDD Can help you in your coding.
TDD can help you produce production-level code, all the while gaining practice in the software development cycle. These tests allow you to develop code quickly and prevent any new code that you add from breaking your application. This is a practice essential to learn as a self-taught developer, as it is a skill that can be used in industry.
How to implement TDD in your projects.
For this implementation, we will focus on python and a basic project of a calculator. The source code for this calculator can be found here on my Github. TDD uses a total of four steps.
- Understanding the final product you are looking to produce including all its features.
- Write unit tests based on each feature needed.
- Implement feature in code, until tests pass.
- Refractor code and repeat.
For python, we will implement this using the library unittest, you can find the documentation for unittest here.
So with this code already being written, I will just show the implementation of unittest. We will be implementing this for all four functions of addition, multiplication, subtraction, and division. So we must first understand what we would like each of these functions to do, which in our case is fairly simple. We then can create unit tests to make sure these functions work as intended and pass our unit tests.
Here are the functions we will be testing.
# Addition
def add(first_number, second_number):
"""Adds two given numbers together"""
total = first_number + second_number
return total
# Subtraction
def subtract(first_number, second_number):
"""Subtracts two given numbers"""
total = first_number - second_number
return total
# Multipliication
def multi(first_number, second_number):
"""Multiplies two given numbers together"""
total = first_number * second_number
return total
# Division
def division(first_number, second_number):
"""Divides two given numbers"""
total = first_number / second_number
return total
These basic functions from the calculator. py file will allow us to test and understand how to use unittest without worrying about the complication of the individual function.
To test these functions we will import the unittest library and the calculator. py file to be tested. If you notice at the bottom of the file we have the unittest.main() running if the file is called to be run. This prevents us from calling the entire function python -m unittest test_calculator.py in the CLI when we are ready to test. Instead, we can just call the file name as python test_calculator.py
import unittest
import calculator
if __name__ == '__main__':
unittest.main()
We will then add our test cases in between those lines of code. Start by creating a class that will inherit from the unittest.Testcase class. We will then test our functions starting with the addition function by using assertEqual and passing it the add function from our original calculator. py file. We want to use numbers that we know are correct to ensure the function is working properly. We also want to add any edge cases we can think of like the addition of negatives, floating-point numbers, or zeros. This ensures we fully test the functionality of each function, and we can raise errors when needed.
import unittest
import calculator
class TestCalculator(unittest.TestCase):
def test_add(self):
self.assertEqual(calculator.add(60, 40), 100.0)
self.assertEqual(calculator.add(-60, 40), -20.0)
self.assertEqual(calculator.add(60.5, 40), 100.5)
if __name__ == '__main__':
unittest.main()
We can then do this for each of our functions writing test cases for each in the same class. This will show a total of four tests, because unittest views each function as one test.
import unittest
import calculator
class TestCalculator(unittest.TestCase):
def test_add(self):
self.assertEqual(calculator.add(60, 40), 100.0)
self.assertEqual(calculator.add(-60, 40), -20.0)
self.assertEqual(calculator.add(60.5, 40), 100.5)
def test_subtract(self):
self.assertEqual(calculator.subtract(60, 40), 20.0)
self.assertEqual(calculator.subtract(-60, 40), -100.0)
self.assertEqual(calculator.subtract(60.5, 40), 20.5)
def test_multiply(self):
self.assertEqual(calculator.multi(60, 40), 2400)
self.assertEqual(calculator.multi(-60, 40), -2400)
self.assertEqual(calculator.multi(60.5, 40), 2420)
def test_division(self):
self.assertEqual(calculator.division(60, 40), 1.5)
self.assertEqual(calculator.division(-60, 40), -1.5)
self.assertEqual(calculator.division(60.5, 40), 1.5125)
if __name__ == '__main__':
unittest.main()
If everything works correctly in our application you should see something like this showing that the tests have passed.

Although this is a very basic application and set of tests, we can see how if we start by designing these tests, if we make any mistakes during coding or addition of code to our codebase. These tests will throw an error, and immediately point us to the location to look to fix the code. This is extremely valuable and allows for faster implementation of features, especially very complicated ones.
In Conclusion
Learning test-driven development most likely will not be the sole reason that you do not get a position at a software company. Most likely you will learn the development cycle and practices that a particular company uses during training. However, as you can see learning TDD will help you implement better code to your projects and help you produce higher-level code. This allows you to stand out from the crowd and show that you're thinking about the entire process of software development.


