Code Kata Walkthrough - Part I
For my next few posts, I am going to do a step by step walkthrough of a Code Kata. The Kata is called “String Calculator” and can be found here. I will be using the RED-GREEN-REFACTOR method where I will first write a failing test case, then write code to make the test case pass, then look over the code and refactor where necessary.
When solving a Code Kata for the first time, it is important not to read ahead. We do not want to see what the full program should be doing, because that will cause us to try and engineer a solution for everything instead of taking it one step at a time. This is essential for TDD. We want to start with the easiest test case, implement the easiest possible solution, and work our way up to more complex test cases from there.
After reading the start of the problem, we know that we are designing a string calculator which will take in a string of numbers and output their sum. The easiest possible test case that we can start with is an empty string. Let’s create the spec class and start writing our first test case:
describe("Calculator", function() {
beforeEach(function() {
calc = new Calculator();
});
it("should return 0 when passed an empty string", function() {
expect(calc.add("")).toBe(0);
});
});
We are in the RED state. This test case fails with a number of errors. Calculator is undefined because we do not have a Calculator class, there is no add method on the nonexistent class, and the nonexistent method does not return the expected result of 0. Let’s implement some code to fix these problems:
function Calculator() {
}
Calculator.prototype.add = function(str) {
return 0;
}
Our test case is now passing. As you can see, I implemented the simplest possible solution to get my test case to pass. As I add more test cases, I will need to refactor this code since returning 0 in every scenario is clearly not going to be an adequate solution in the long run. This may seem inefficient, but the goal is not to be efficient. The goal is to write good code that works. By breaking large problems into very tiny slices and just implementing enough code to solve that one problem, we are reducing the number of things that we need to think about at one time and limiting the scope of our problem. We will be attempting to refactor our code with every test case. This forces us to revisit code multiple times, determine if it is the best solution for the test cases to that point, and refactor the code if it is not.
Now let’s move on to the next test case. The next simplest test case that I can think of is an input of one number. The sum of one number should be itself. Let’s add this test case to our spec:
it("should return the input number when passed only one number", function() {
expect(calc.add("5")).toBe(5);
expect(calc.add("8")).toBe(8);
expect(calc.add("14")).toBe(14);
expect(calc.add("105")).toBe(105);
});
We are back in the RED state. This test case fails because our method returns 0 every single time, which obviously is not the sum of any single number other than 0. Let’s implement some code to fix this test case:
Calculator.prototype.add = function(str) {
if(str.length == 0) {
return 0;
} else {
return parseInt(str);
}
}
We are back in the GREEN state with both of our test cases passing. Anything that is not an empty string will return itself. Let’s move on to the REFACTOR phase. This code is still very trivial, so nothing to refactor here. Let’s move on to the next test case, which is to take in two numbers and return their sum.
it("should return the sum of two numbers when passed two numbers separated by a comma", function() {
expect(calc.add("5,4")).toBe(9);
});
This test case fails, because we have no code to handle two numbers. Let’s add the code to fix our test case:
Calculator.prototype.add = function(str) {
if(str.length == 0) {
return 0;
} else if(str.includes(",")) {
nums = str.split(",");
return parseInt(nums[0]) + parseInt(nums[1]);
} else {
return parseInt(str);
}
}
With this code, we are back in the GREEN state. The code is still very simple, so nothing to refactor at this point. I don’t want to make this post too long, so I will wrap it up here. My next post will continue with this problem!