TDD Code walk through

This page walks you through a simple TDD kata

 

Pre-requisite knowledge

  • How to install a unit testing framework for the particular language

  • An understanding of why testing is important

  • How to write a unit test and run the test

  • The three A's (Arrange, Act, Assert)

  • Writing incremental tests and why this is important - one test, some production code, then next test and some production code

The problem statement

A user can send from a front end application a series of comma separated words to be logged as part of a wider application. You can assume that there already exists some code that sends a cleansed array of words to the business/database tier - these are referred to as tags. You must design an implementation class (the CUT) that splits that string into an array of strings. You will need to write the tests first, then the production code.

Overview

You are going to develop a small piece of code that accepts a comma delimited list of tags (words/tokens - characters, numbers, some symbols like $£%& but not a comma) and must return an array of tags. The preceding and proceeding commas should be removed, white spaces preceding the first tag must be removed, white spaces proceeding the last tag must be removed, contiguous series of words separated with spaces and ended with a comma should be treated as single tag

Examples of the rules are as follows

  • "java" must return ["java"]

  • "java, python" must return ["java", "python"]

  • "java, C#, python" must return ["java", “C#”, "python"]

  • " java" must return ["java"]

  • ",java" must return ["java"]

  • "java," must return ["java"]

  • "java byte code, python" must return ["java byte code", "python"]

Constructs your tests in the following order

Test 1

def test_split_empty_string_result_empty_array(self): # arrange stringToSplit = "" regex = "" expResult = [] result = None cut = TagManipulator() # act result = cut.parse_string(stringToSplit, regex) # assert assert result == expResult

Implementation code

def parse_string(self, tags, regex): result = [] if len(tags) < 1 : return result return result

If you are asking yourself “is this all the code should do?” Good question, we never write anymore code than is required to pass the test. If we do, then we have production code for which there are no tests.

Test 2

def test_split_comma_empty_string_result_empty_array(self): # arrange stringToSplit = "," regex = ",\\ *" expResult = [] result = None cut = TagManipulator() # act result = cut.parse_string(stringToSplit, regex) # assert assert result == expResult

Implementation code

Test 3

Implementation code

In this CUT we have refactored the code to use the regular expression function split()

Test 4

Implementation code

CUT doesn't change. Someone may ask "but I thought we only write enough to pass the test?" We did, but a side effect it that this other test also passes. it doesn't mean it's wrong. If the test did fail, then we would have to change the CUT to pass the test.

Test 5

Implementation code

CUT does not change

Test 6

Implementation code

Test 7

Implementation code

CUT does not change

Test 8

Implementation code

CUT does not change

Test 9

Implementation code

CUT does not change

Test 10

Implementation code

CUT does not change

Test 11

Implementation code

 

You could argue that where the CUT does not change, the tests could be merged into a single test with multiple asserts within that test.