Skip to content

Instantly share code, notes, and snippets.

@sviataslau
Created January 28, 2013 06:19
Show Gist options
  • Save sviataslau/4653422 to your computer and use it in GitHub Desktop.
Save sviataslau/4653422 to your computer and use it in GitHub Desktop.
Sample String Calculator TDD Kata implementation. http://osherove.com/tdd-kata-1/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace StringCalculator
{
public class Calculator
{
private static readonly Regex _inputParsingRegex = new Regex(@"(?<definition>//(?<delimiter>.+)\n).*");
private string _input;
public int Add(string input)
{
if (input == null)
throw new ArgumentNullException("input");
if (input == string.Empty)
return 0;
_input = input;
var numbers = Parse();
EnsureNonNegatives(numbers);
return numbers.Sum();
}
private int[] Parse()
{
var delimiters = GetAllAllowedDelimiters();
var numbersInput = GetNumberInput().Split(delimiters).Where(n => !string.IsNullOrEmpty(n));
int[] numbers = Filter(numbersInput.Select(int.Parse).ToArray());
return numbers;
}
private static int[] Filter(IEnumerable<int> numbers)
{
return numbers.Where(n => n <= 1000).ToArray();
}
private static void EnsureNonNegatives(IEnumerable<int> numbers)
{
int[] negatives = numbers.Where(n => n < 0).ToArray();
if (negatives.Any())
{
string exceptionMessage = string.Format("negative not allowed:{0}", string.Join(",", negatives));
throw new ArgumentException(exceptionMessage);
}
}
private IEnumerable<string> GetUserDefinedDelimiters()
{
var match = _inputParsingRegex.Match(_input);
string delimiter = match.Groups["delimiter"].Value;
if (string.IsNullOrEmpty(delimiter))
return new string[0];
return delimiter.Split(new[] {'[', ']'});
}
private char[] GetAllAllowedDelimiters()
{
var delimiter = GetUserDefinedDelimiters();
return new[] { ',', '\n' }.Concat(delimiter.SelectMany(d => d.ToCharArray())).ToArray();
}
private string GetDelimiterDefinitionPart()
{
var match = _inputParsingRegex.Match(_input);
return match.Groups["definition"].Value;
}
private string GetNumberInput()
{
string delimiterDefinitionPart = GetDelimiterDefinitionPart();
return !string.IsNullOrEmpty(delimiterDefinitionPart) ? _input.Replace(delimiterDefinitionPart, string.Empty) : _input;
}
}
}
using System;
using FluentAssertions;
using NUnit.Framework;
namespace StringCalculator.Tests
{
public class Tests
{
[Test]
public void should_return_sum_for_empty()
{
var calculator = new Calculator();
int result = calculator.Add("");
result.Should().Be(0);
}
[Test]
public void should_return_sum_for_one_number()
{
var calculator = new Calculator();
int result = calculator.Add("10");
result.Should().Be(10);
}
[Test]
public void should_return_sum_for_two_numbers()
{
var calculator = new Calculator();
int result = calculator.Add("10,11");
result.Should().Be(21);
}
[Test]
public void should_return_sum_for_three_numbers()
{
var calculator = new Calculator();
int result = calculator.Add("10,11,12");
result.Should().Be(33);
}
[Test]
public void should_return_sum_for_unknown_amount_of_numbers()
{
var calculator = new Calculator();
int result = calculator.Add("1,2,3,4,5");
result.Should().Be(15);
}
[Test]
public void should_return_sum_both_with_command_and_newline_separators()
{
var calculator = new Calculator();
int result = calculator.Add("1\n2,3");
result.Should().Be(6);
}
[Test]
public void should_return_sum_with_custom_delimiter()
{
var calculator = new Calculator();
int result = calculator.Add("//;\n1;2");
result.Should().Be(3);
}
[Test]
public void should_throw_exception_if_negative_numbers_are_passed()
{
var calculator = new Calculator();
Action call = () => calculator.Add("-1,10,-2");
call.ShouldThrow<ArgumentException>("negative not allowed:-1,-2");
}
[Test]
public void should_ignore_numbers_greater_than_1000()
{
var calculator = new Calculator();
int result = calculator.Add("10,2,1001");
result.Should().Be(12);
}
[Test]
public void should_return_sum_with_custom_delimiter_with_any_length()
{
var calculator = new Calculator();
int result = calculator.Add("//***\n1***2***3");
result.Should().Be(6);
}
[Test]
public void should_return_sum_with_more_than_one_custom_delimiter()
{
var calculator = new Calculator();
int result = calculator.Add("//[*][%]\n1*2%3");
result.Should().Be(6);
}
[Test]
public void should_return_sum_with_more_than_one_custom_delimiter_with_any_length()
{
var calculator = new Calculator();
int result = calculator.Add("//[***][%]\n1***2%3");
result.Should().Be(6);
}
[Test]
public void should_return_sum_wht_both_custom_and_standard_delimiters()
{
var calculator = new Calculator();
int result = calculator.Add("//[***][%]\n1***2,3%4\n5");
result.Should().Be(15);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment