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.