Categories
Blog Software Development

Test Driven Development – A practical Example

Step Two

Now that we have completed the first item, the list looks like this:

  • Invalid Input
  • Addition
  • Subtraction
  • Multiplication
  • Division
  • Bracketed Expressions

We continue with handling invalid input. Whenever input is not a valid mathematical expression, the program shall show an error message. For this, we write a test case that checks several examples of invalid input.

def test_invalid_input(self):
    app = CalculatorApplication()

    output = app.calculate("+")
    self.assertEqual(output, "Invalid expression")

    output = app.calculate("3 +")
    self.assertEqual(output, "Invalid expression")

    output = app.calculate("5 * -")
    self.assertEqual(output, "Invalid expression")

    output = app.calculate("7 81")
    self.assertEqual(output, "Invalid expression")

Let’s run these two test. We expect the new test to fail. And it does.

FAIL: test_invalid_input (testCalculatorApplication.TestCalculatorApplication)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../testCalculatorApplication.py", line 14, in test_invalid_input
    self.assertEqual(output, "Invalid expression")
AssertionError: '0' != 'Invalid expression'
- 0
+ Invalid expression


----------------------------------------------------------------------
Ran 2 tests in 0.001s

The test is right – the current implementation of calculate always returns “0”, which makes this test fail. To make the test pass, we have to distinguish between an empty input and an invalid input. What makes up an invalid input? Let’s fake it by doing the simplest solution: An empty string is valid, a non-empty string is invalid. This will make the tests pass. We will do the real implementation in a later step when we add the first test for valid input.

class CalculatorApplication():
    def calculate(self, expression):
        if(expression == ""):
            return "0"
        else:
            return "Invalid expression"

This simple implementation makes both tests green. Next comes the refactoring phase.

There is some duplication in the unit tests. Both tests create an instance of CalculatorApplication to use for the test. This can be done in a setUp method, so the code only appears once. The method setUp will be called by the test framework before each test. With our implementation of setUp, each test will have a freshly prepared instance of CalculatorApplication.

import unittest
from CalculatorApplication import CalculatorApplication

class TestCalculatorApplication(unittest.TestCase):
    def setUp(self):
        self.app = app = CalculatorApplication()

    def test_empty_input(self):
        output = self.app.calculate("")
        self.assertEqual(output, "0")

    def test_invalid_input(self):
        output = self.app.calculate("+")
        self.assertEqual(output, "Invalid expression")

        output = self.app.calculate("3 +")
        self.assertEqual(output, "Invalid expression")

        output = self.app.calculate("5 * -")
        self.assertEqual(output, "Invalid expression")

        output = self.app.calculate("7 81")
        self.assertEqual(output, "Invalid expression")

Summary of Step Two

We have written a new test case and added some functionality. Also, we have done the first refactoring. To make the test pass we cheated a little bit. This cheat will be removed in the next step.

As this step is over, let’s cross “Invalid Input” of the list.

Leave a Reply

Your email address will not be published. Required fields are marked *