Previous part in the series TDD – commit by commit String Calculator Kata (III) Last time I finished on negative values requirement. This will be the “almost” last part of the series about String Calculator Kata. There was some interest in more practical TDD examples in business context with services, layers, mocks and external dependencies like database. I will start next series to cover scenarios like that. Current kata is quite simple and was just a start.

Step - Ignore numbers greater or equal to 1000

[Test]
public void Sum_ignores_number_greater_than_1000()
{
    var calculator = new StringCalculator();
    var value = "1,2000,1000,1003";

    var actual = calculator.SumFromString(value);

    var expected = 1;

    Assert.That(actual, Is.EqualTo(expected));
}

I am expecting 1 here, all the other values are above 1000.

Commit - Test
Based on changes from previous steps, I can just use .Where() clause to filter out the values.

 x < 1000).Sum();

Commit - Implementation

Step - Support for delimiters with any length Delimiters can be of any length with the following format

”//;;;\n”. Based on this input delimiter would be ‘;;;’, of course previous requirements are still supported.

[Test]
public void MultiCharDelimeter_Is_Supported()
{
     var calculator = new StringCalculator();
     var value = "//;;;\n1;;;2;;;3;;;4";

     var actual = calculator.Sum(value);

     var expected = 10;

     Assert.That(actual, Is.EqualTo(expected));
}

Commit - Test
There are two options. I can either use brute force customised index based parsing, or I can use Regular expression. I know that if you have a problem and solve it with regexp, you suddenly have two problems :) Still I think that regexp should be fine in here.

.*)", RegexOptions.Compiled);

This regexp matches two groups ‘delimiter’ and ‘value’. I need ‘value’ to extract the string with all the values. As an examples, given “//;;;\n1;;;2;;;3;;;4” delimiter - ;;; value -1;;;2;;;3;;;4 With this solution I will remove substring based extraction which was not that good.

Commit - Implementation
To support delimiter of multiple chars, I changed all of the collections from ‘IEnumerable char’ to ‘IEnumerable string’. You can notice that there is a .Single(), that’s because, based on current format and requirement, I expect only single value and delimiter. I still don’t like ref keyword in ‘ExtractDelimiters’ method, it’s an anti pattern. The only place I can think positively about ‘ref / out’ usability is ‘.TryParse()’ method. To avoid ‘ref’, ‘ExtractDelimiters’ function must return more than only single string. I can either use Tuple or create new class that would encapsulate both extracted delimiters and value. To make this simple, I decided to use ‘Tuple’. Commit - Ref to Tuple I also don’t like this line of code. It just too complicated and unreadable.

 m.Groups["delimeter"].Value).Single();

Much nicer solution would be to hide this logic and extract the method.

var delim = this.ExtractRegexpGroup(matches, "delimeter");
extractedInput = this.ExtractRegexpGroup(matches, "value");

Commit - extract regexp matches method

Step - multiple delimiters

[Test]
public void Multiple_Delimeters_Test()
{
    var calculator = new StringCalculator();
    var value = "//[*][%]\n1*2%3";

    var actual = calculator.Sum(value);

    var expected = 6;

    Assert.That(actual, Is.EqualTo(expected));
}

Commit - Test Previous solution supports only one delimiter, now We can have many delimiters separated by brackets. Current regexp in this scenario treats “[*][%]” as single delimiter. i just need to add method / regexp that will extract delimeters from this string.

new Regex("\\[(.?)\\]"))

This Regexp will take [&][] string and extract both ‘&’ and ‘’.

Commit - Implementation It is easy to make a mistake with regexp, I am not sure if all the extractions work fine, that’s why i want to explore more test cases with mutliple delimiters, and multi char delimiters. To do this I ll just convert previous Test to TestCases with variables.

[TestCase(";;;")]
[TestCase("x")]
[TestCase("-----")]
[TestCase("123456789")]
[TestCase("              ")]
[TestCase("\r")]
[TestCase("()()()")]
[TestCase("((()))")]
[TestCase("[email protected]#$%^^&*()_+/|`~")]
public void MultiCharDelimeter_Is_Supported(string delimiter)
{
    var calculator = new StringCalculator();
    var value = string.Format("//{0}\n1{0}2{0}3{0}4", delimiter);

    var actual = calculator.SumFromString(value);

    var expected = 10;

    Assert.That(actual, Is.EqualTo(expected));
}

Commit - Test Some of the delimiters looks werid, but still all the tests pass, thats a good message.

[TestCase(";;;","   ")]
[TestCase("x"," u")]
[TestCase("-----", "---123")]
[TestCase("123456789", "090()")]
[TestCase("              ", "")]
[TestCase("\r", "12x")]
[TestCase("()()()", "^^&*")]
[TestCase("[email protected]#$%^^&*()_+/|`~", "123456")]
public void Multiple_Delimeters_Test(string delimiter1, string delimeter2)
{
    var calculator = new StringCalculator();
    var value = string.Format("//[{0}][{1}]\n1{0}2{1}3{0}4", delimiter1, delimeter2);

    var actual = calculator.SumFromString(value);

    var expected = 6;

    Assert.That(actual, Is.EqualTo(expected));
}

Edge cases mutliple chars.
Wow with this change ale the tests are red. There has to be some problem with regexp. The extraction of single delimiters does only work for single char delimiters. I had to do little change.

Fix for edge cases
That would be all for now. In the last post of this serie, I will do a little cleanup with some summary of whole series. I need some advice from you dear reader: - how do you feel about using regexp, any other cool solution to do all the extractions ? - do you think that usage of Tuple is ok here, or should I create a new container class in this scenario ?