Created
January 30, 2013 07:02
-
-
Save sviataslau/4671317 to your computer and use it in GitHub Desktop.
Sample String Calculator TDD Kata 2 implementation. http://osherove.com/tdd-kata-2/
Based on Kata implementation from http://osherove.com/tdd-kata/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Globalization; | |
using System.Linq; | |
using System.Text.RegularExpressions; | |
namespace StringCalculator | |
{ | |
public class Calculator : ICalculator | |
{ | |
private readonly IIOProvider _iio; | |
private static readonly Regex _inputParsingRegex = new Regex(@"(?<definition>//(?<delimiter>.+)\n).*"); | |
private string _input; | |
public Calculator() | |
{ | |
} | |
public Calculator(IIOProvider iio) | |
{ | |
_iio = iio; | |
} | |
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); | |
int sum = numbers.Sum(); | |
OutValue(sum); | |
return sum; | |
} | |
private void OutValue(int sum) | |
{ | |
if (_iio != null) | |
_iio.Out(sum.ToString(CultureInfo.InvariantCulture)); | |
} | |
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; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace StringCalculator | |
{ | |
public interface ICalculator | |
{ | |
int Add(string input); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace StringCalculator | |
{ | |
public interface IIOProvider | |
{ | |
void Out(string message); | |
string GetInput(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace StringCalculator.Tests | |
{ | |
public class Program | |
{ | |
private readonly ICalculator _calculator; | |
private readonly IIOProvider _iio; | |
public Program(ICalculator calculator, IIOProvider iio) | |
{ | |
_calculator = calculator; | |
_iio = iio; | |
} | |
public void Execute() | |
{ | |
while (true) | |
{ | |
string input = _iio.GetInput(); | |
if (input == Environment.NewLine) | |
break; | |
int result = _calculator.Add(input); | |
_iio.Out(string.Format("The result is {0}", result)); | |
_iio.Out("another input please"); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using FluentAssertions; | |
using NSubstitute; | |
using NUnit.Framework; | |
namespace StringCalculator.Tests | |
{ | |
public class Tests | |
{ | |
private ICalculator _calculator; | |
private IIOProvider _io; | |
[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); | |
} | |
[Test] | |
public void should_output_sum_to_console() | |
{ | |
var output = Substitute.For<IIOProvider>(); | |
var calculator = new Calculator(output); | |
calculator.Add("1,2"); | |
output.Received().Out("3"); | |
} | |
[Test] | |
public void exec_should_out_to_contole() | |
{ | |
var program = new Program(_calculator, _io); | |
program.Execute(); | |
_io.Received().Out("The result is 6"); | |
} | |
[Test] | |
public void exec_should_ask_for_next_input() | |
{ | |
var program = new Program(_calculator, _io); | |
program.Execute(); | |
_io.Received().Out("The result is 6"); | |
_io.Received().Out("another input please"); | |
} | |
[Test] | |
public void exec_should_wait_for_next_input() | |
{ | |
_calculator.Add("4,5,6").Returns(15); | |
_calculator.Add("7,8,9").Returns(24); | |
_io.GetInput().Returns(x => "1,2,3", x => "4,5,6", x => "7,8,9", x => Environment.NewLine); | |
var program = new Program(_calculator, _io); | |
program.Execute(); | |
_io.Received().Out("The result is 6"); | |
_io.Received().Out("another input please"); | |
_io.Received().Out("The result is 15"); | |
_io.Received().Out("another input please"); | |
_io.Received().Out("The result is 24"); | |
_io.Received().Out("another input please"); | |
} | |
[SetUp] | |
public void Setup() | |
{ | |
_calculator = Substitute.For<ICalculator>(); | |
_calculator.Add("1,2,3").Returns(6); | |
_io = Substitute.For<IIOProvider>(); | |
_io.GetInput().Returns(x => "1,2,3", x => Environment.NewLine); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment