Unlocking the Power of Test-Driven Development (TDD)

Sahan Dilshan
3 min readOct 22, 2023

--

Test Driven Development (TDD) is more than just a development methodology; it’s a mindset. With TDD, we reverse the traditional flow of software development. Instead of writing code and then testing it, we start with the tests. This paradigm shift not only helps catch bugs early but also ensures that our codebase remains clean and maintainable.

What is TDD?

At its core, TDD involves three simple steps:

  1. Write a test: Before writing any code, you define the expected behavior of your feature in a test.
  2. Run the test: Naturally, it will fail since you haven’t implemented the feature yet.
  3. Write the code: Develop your feature just enough to make the test pass.

This cycle repeats until all features and functionalities are fully implemented. It’s essential to note that TDD isn’t about testing; it’s about designing your software.

Why TDD?

  • Early Bug Detection: Catch and fix errors before they become more significant problems.
  • Improved Design: Writing tests first can lead to more modular and maintainable code.
  • Peace of Mind: Changes and refactors become less daunting with a robust test suite.

Building a Calculator, One Operation at a Time

To truly grasp the TDD methodology, let’s construct a basic calculator, focusing on one arithmetic operation at a time.

  1. Addition
  • Test
def test_addition(self):
self.assertEqual(self.calc.add(5, 3), 8)

Here, we’re asserting that our yet-to-be-created add method should return 8 when adding 5 and 3.

  • Implementation:
def add(self, a, b):
return a + b

With this straightforward implementation, our addition test should now pass!

2. Subtraction

  • Test
def test_subtraction(self):
self.assertEqual(self.calc.subtract(5, 3), 2)

This test sets the expectation that subtracting 3 from 5 using our subtract method should yield 2.

  • Implementation:
def subtract(self, a, b):
return a - b

With the subtraction functionality in place, the corresponding test should pass seamlessly.

3. Multiplication

  • Test
def test_multiplication(self):
self.assertEqual(self.calc.multiply(5, 3), 15)

Here, the test anticipates our multiply method to return 15 when multiplying 5 by 3.

  • Implementation
def multiply(self, a, b):
return a * b

Implementing the multiplication ensures that our test for this operation now succeeds.

4. Division

  • Test
def test_simple_division(self):
self.assertEqual(self.calc.divide(6, 3), 2)

def test_division_by_zero(self):
with self.assertRaises(ValueError):
self.calc.divide(6, 0)

The first test (test_simple_division) checks if our divide method returns 2 when dividing 6 by 3. The second test (test_division_by_zero) expects a ValueError to be raised when attempting to divide by zero.

  • First Implementation

At this stage, we’ll only address the simple division, without any handling for division by zero.

def divide(self, a, b):
return a / b
  • Improved Implementation

To make both our tests pass, we’ll now incorporate a check for division by zero.

def divide(self, a, b):
if b == 0:
raise ValueError("Cannot divide by zero!")
return a / b

With this implementation, both test_simple_division and test_division_by_zero should pass, demonstrating the iterative nature of TDD. We started with a basic implementation, identified its shortcomings through testing, and then refined our code.

Conclusion

Test Driven Development, demonstrated through our step-by-step calculator example, is a powerful paradigm. By iteratively writing tests and corresponding implementations, you can be confident in the robustness and functionality of your code. Adopt TDD, and elevate the quality and reliability of your software projects.

--

--

Sahan Dilshan

Software Engineer @WSO2 | NLP/Deep Learning Enthusiastic