Test Driven Development (series) - String calculator #1
How many times you had to comment a test so you can release in production ASAP?
Intro
For a few years now, every job description, recruiter and interview mention in a way or another the concept of TDD (Test-Driven Development). ✍️
Short description: TDD is a software development process in which the developer first writes a fully automated test case before writing the actual code to fulfil that test. And then, write and refactor the code.
If you've never written code in this way, it might be a bit of a struggle at first to not just go ahead and write the implementation.
But if you stick with the process you'll see that it's a really efficient way of coding.
That's why, we'll showcase this process through a simple application. 💯
Brief
Let's create a simple String Calculator
that should be able to receive up to two numbers, separated by commas, and will return their sum.
The method signature will be: int add(String numbers)
Example:
input = "1,2"
->result = 3
input = "1"
->result = 1
input = ""
->result = 0
Implementation
🔵 First, let's create the StringCalculator
class and the method skeleton.
public class StringCalculator {
public int add(String numbers) {
return null;
}
}
🚀 This is the point where, instead of writing the implementation of the method, we first write a test for it.
public class StringCalculatorTest {
@Test
void testCalculator_TwoNumbers() {
StringCalculator stringCalculator = new StringCalculator();
String input = "1,2";
Assertions.assertEquals(3, stringCalculator.add(input));
}
}
🔴 Being that we don't have any logic implemented yet, the test will fail.
🔵 Now, we'll go back to the StringCalculator
class and write some code. We'll change the method to:
public class StringCalculator {
public int add(String input) {
int sum = 0;
String[] numbers = input.split(",");
for (String number : numbers) {
sum += Integer.parseInt(number);
}
return sum;
}
}
🟢 Next, we go back to the test, run it and see that the test is successful now. We are now ready to add a new test.
@Test
void testCalculator_SingleNumber() {
StringCalculator stringCalculator = new StringCalculator();
String input = "1";
Assertions.assertEquals(1, stringCalculator.add(input));
}
🟢 This test will also run successfully so we can move to the next condition. We add a new test for it.
@Test
void testCalculator_EmptyString() {
StringCalculator stringCalculator = new StringCalculator();
String input = "";
Assertions.assertEquals(0, stringCalculator.add(input));
}
🔴 The test will fail.
🔵 That means we need to add some new changes in code.
public class StringCalculator {
public int add(String input) {
if(input.isEmpty()) {
return 0;
}
int sum = 0;
String[] numbers = input.split(",");
for (String number : numbers) {
sum += Integer.parseInt(number);
}
return sum;
}
}
🟢 Run the test again and see it turning green.
As you can see, the flow to this process is Test -> Add code and refactor -> Test -> Repeat
The advantage of this process is that it forces you to keep refactoring the code while writing the tests.
This is because you need to keep the code lean and simple in order to be easy testable.
Stay tuned to this series so that you can see how the code evolves when adding more conditions but still keeping it maintainable and loosely coupled.
Not to mention the test coverage percentage. 👀
Stay tuned! 🚀