How Test-Driven-Development works (Part 3)
This is part 3 of the blog series about how TDD (Test-Driven-Development) works. To start from the beginning you can refer to the start of the blog series here.
We will continue our journey of TDD with the upcoming requirements to evolve our code along with the new tests.
Requirement 4: Allow the Add method to handle an unknown amount of numbers
[Java Test]
// Test 1
// @Test(expected = RuntimeException.class)
// public final void whenMoreThan2NumbersAreUsedThenExceptionIsThrown(){
// add("1,2,3");
// }
@Test
public final void whenAnyNumberOfNumbersIsUsedThenReturnValuesAreTheirSums() {
Assert.assertEquals(3+6+15+18+46+33, StringCalculator.add("3,6,15,18,46,33"));
}
The above mentioned new test will fail with the existing code base as we have not implemented the functionality of handling unknown numbers to be summed. The first test is no more required as our requirement has been changed therefore we comment it out.
Now we will refactor our code to implement the functionality of handling the sum of an unknown amount of numbers
[Java Implementation]
public static int add(final String numbers){
int returnValue = 0;
String[] numbersArray = numbers.split(",");
// Comment out the functionality of handling only 2 numbers
/*if (numbersArray.length > 2) {
// throw new RuntimeException("Up to 2 numbers separated by comma (,) are allowed");
} else {*/
for (String number : numbersArray) {
if (!number.isEmpty()) {
returnValue += Integer.parseInt(number); // If it is not a number, parseInt will throw an exception
}
}
//}
return returnValue; // Added return statement
}
What we did is remove the existing functionality of handling the exception of more than two numbers. So after cleaning our code, our method will look as follows.
public static int add(final String numbers) {
int returnValue = 0;
String[] numbersArray = numbers.split(",");
for (String number : numbersArray) {
if (!number.isEmpty()) {
returnValue += Integer.parseInt(number);
}
}
return returnValue;
}
Now when we rerun our tests all tests will pass as our requirement has been changed from handling only two numbers to handling an unknown amount of numbers.
Requirement 5: Allow the Add method to handle newlines between numbers (instead of commas).
[Java Test]
@Test
public final void whenNewLineIsUsedBetweenNumbersThenReturnValuesAreTheirSums() {
Assert.assertEquals(2+4+18, StringCalculator.add("2,4n18"));
}
[Java Implementation]
public static int add(final String numbers) {
int returnValue = 0;
String[] numbersArray = numbers.split(",|n"); // Added |n to the split regex
for (String number : numbersArray) {
if (!number.isEmpty()) {
returnValue += Integer.parseInt(number);
}
}
return returnValue;
}
To fulfill this requirement we just need to extend the split regex by adding |\n.
Requirement 6: Support different delimiters
In this requirement, it is specified that a user can use a different delimiter other than a comma which should also be accommodated by our String calculator. To specify the desired delimiter, it has to be at the beginning of the string that should look like this: “//[delimiter]\n[numbers…]” for example “//;\n4;6” should take 4 and 6 as parameters and return 10 where the default delimiter is “;”.
[Java Test]
// Requirement 6: Support different delimiters
@Test
public final void whenDelimiterIsSpecifiedThenItIsUsedToSeparateNumbers() {
Assert.assertEquals(3+6+15, StringCalculator.add("//;n3;6;15"));
}
[Java Implementation]
public static int add(final String numbers) {
String delimiter = ",|n";
String numbersWithoutDelimiter = numbers;
if (numbers.startsWith("//")) {
int delimiterIndex = numbers.indexOf("//") + 2;
delimiter = numbers.substring(delimiterIndex, delimiterIndex + 1);
numbersWithoutDelimiter = numbers.substring(numbers.indexOf("n") + 1);
}
return add(numbersWithoutDelimiter, delimiter);
}
private static int add(final String numbers, final String delimiter) {
int returnValue = 0;
String[] numbersArray = numbers.split(delimiter);
for (String number : numbersArray) {
if (!number.trim().isEmpty()) {
returnValue += Integer.parseInt(number.trim());
}
}
return returnValue;
}
For this requirement implementation, we did quite a lot of refactoring. We split the code into 2 methods. The initial method parses the input looking for the delimiter and later on calls the new private one that does the actual sum. Since we already have tests that cover all existing functionality, it was safe to do the refactoring. If anything went wrong, one of the tests would find the problem.
References:
The example of the string calculator has been taken from the Roy Osherove Katas. Don’t click the link until you’re finished with other parts of this blog series. This exercise is best done when not all requirements are known in advance.
1 Comment
Working at Walmart · November 8, 2022 at 10:10 pm
Great post