First write an (initially failing) automated test case that defines a desired improvement or new function, then produces the minimum amount of code to pass that test, and finally refactors the new code to acceptable standards.
. Do not click the link until you’re finished with this article. This excercise is best done when not all requirements are known in advance.
Below you will find the test code related to each requirement and afterwards the actual implementation. Try to read only one requirement, write the tests and the implementation yourself and compare it with the results from this article. Remember that there are many different ways to write tests and implementation. This article is only one out of many possible solutions.
Let’s start!
Requirements
Create a simple String calculator with a method int Add(string numbers)
The method can take 0, 1 or 2 numbers, and will return their sum (for an empty string it will return 0) for example “” or “1” or “1,2”
Allow the Add method to handle an unknown amount of numbers
Allow the Add method to handle new lines between numbers (instead of commas).
The following input is ok: “1\n2,3” (will equal 6)
Support different delimiters
To change a delimiter, the beginning of the string will contain a separate line that looks like this: “//[delimiter]\n[numbers…]” for example “//;\n1;2” should return three where the default delimiter is ‘;’ .
The first line is optional. All existing scenarios should still be supported
Calling Add with a negative number will throw an exception “negatives not allowed” – and the negative that was passed. If there are multiple negatives, show all of them in the exception message
stop here if you are a beginner.
Numbers bigger than 1000 should be ignored, so adding 2 + 1001 = 2
Delimiters can be of any length with the following format: “//[delimiter]\n” for example: “//[—]\n1—2—3” should return 6
Allow multiple delimiters like this: “//[delim1][delim2]\n” for example “//[-][%]\n1-2%3” should return 6.
Make sure you can also handle multiple delimiters with length longer than one char
Even though this is a very simple program, just looking at those requirements can be overwhelming. Let’s take a different approach. Forget what you just read and let us go through the requirements one by one.
Create a simple String calculator
Requirement 1: The method can take 0, 1 or 2 numbers separated by comma (,).
with When [ACTION] Then [VERIFICATION]. In this case the name of one of the test methods is whenMoreThan2NumbersAreUsedThenExceptionIsThrown.
Our first set of tests verifies that up to two numbers can be passed to the calculator’s add method. If there’s more than two or if one of them is not a number, exception should be thrown. Putting “expected” inside the @Test annotation tells the JUnit runner that the expected outcome is to throw the specified exception. From here on, for brevity reasons, only modified parts of the code will be displayed. Whole code divided into requirements can be obtained from the GitHub repository (
public static final void add(final String numbers) {
String[] numbersArray = numbers.split(",");
if (numbersArray.length > 2) {
throw new RuntimeException("Up to 2 numbers separated by comma (,) are allowed");
} else {
for (String number : numbersArray) {
Integer.parseInt(number); // If it is not a number, parseInt will throw an exception
}
}
}
}
Keep in mind that the idea behind TDD is to do the necessary minimum to make the tests pass and repeat the process until the whole functionality is implemented. At this moment we’re only interested in making sure that “the method can take 0, 1 or 2 numbers”. Run all the tests again and see them pass.
Requirement 2: For an empty string the method will return 0
[JAVA TEST]
@Test
public final void whenEmptyStringIsUsedThenReturnValueIs0() {
Assert.assertEquals(0, StringCalculator.add(""));
}
[JAVA IMPLEMENTATION]
public static final int add(final String numbers) { // Changed void to int
String[] numbersArray = numbers.split(",");
if (numbersArray.length > 2) {
throw new RuntimeException("Up to 2 numbers separated by comma (,) are allowed");
} else {
for (String number : numbersArray) {
if (!number.isEmpty()) {
Integer.parseInt(number);
}
}
}
return 0; // Added return
}
All there was to do to make this test pass was to change the return method from void to int and end it with returning zero.
Requirement 3: Method will return their sum of numbers
[JAVA TEST]
@Test
public final void whenOneNumberIsUsedThenReturnValueIsThatSameNumber() {