Writing Fizzbuzz Kata

In the current Tech era we must test our code as a unit test. TDD katas is a way to ensure that we are delivering good and stable/reliable code. Here we'll write a simple C# program and then write its Kata.

The followings are the basic requirements:

Write a program that prints the numbers from 1 to 100. But for multiples of three print "Fizz" instead of the number and for the multiples of five print "Buzz". For numbers that are multiples of both three and five print "FizzBuzz".

How to write the code

We need a procedure to write the program.

Let's divide this into the following multiple steps so we can easily write and test this:

  1. public static string PrintFizzBuzz()
  2. {
  3. var resultFizzBuzz = string.Empty;
  4. resultFizzBuzz = GetNumbers(resultFizzBuzz);
  5. return resultFizzBuzz;
  6. }
In the preceding, we wrote a simple PrintFizzBuzz() method that is doing the stuff. So, follow our procedure and the following is the complete code:
  1. public class FizzBuzz
  2. {
  3. #region Public Methods
  4. public static string PrintFizzBuzz()
  5. {
  6. var resultFizzBuzz = string.Empty;
  7. resultFizzBuzz = GetNumbers(resultFizzBuzz);
  8. return resultFizzBuzz;
  9. }
  10. public static string PrintFizzBuzz(int number)
  11. {
  12. CanThrowArgumentExceptionWhenNumberNotInRule(number);
  13. var result = GetFizzBuzzResult(number);
  14. if (string.IsNullOrEmpty(result))
  15. result = GetFizzResult(number);
  16. if (string.IsNullOrEmpty(result))
  17. result = GetBuzzResult(number);
  18. return string.IsNullOrEmpty(result) ? number.ToString(CultureInfo.InvariantCulture) : result;
  19. }
  20. #endregion
  21. #region Private Methods
  22. private static string GetFizzBuzzResult(int number)
  23. {
  24. string result = null;
  25. if (IsFizz(number) && IsBuzz(number)) result = "FizzBuzz";
  26. return result;
  27. }
  28. private static string GetBuzzResult(int number)
  29. {
  30. string result = null;
  31. if (IsBuzz(number)) result = "Buzz";
  32. return result;
  33. }
  34. private static string GetFizzResult(int number)
  35. {
  36. string result = null;
  37. if (IsFizz(number)) result = "Fizz";
  38. return result;
  39. }
  40. private static void CanThrowArgumentExceptionWhenNumberNotInRule(int number)
  41. {
  42. if (number > 100 || number < 1)
  43. throw new ArgumentException(
  44. string.Format(
  45. "entered number is [{0}], which does not meet rule, entered number should be between 1 to 100.", number));
  46. }
  47. private static string GetNumbers(string resultFizzBuzz)
  48. {
  49. for (var i = 1; i <= 100; i++)
  50. {
  51. var printNumber = string.Empty;
  52. if (IsFizz(i)) printNumber += "Fizz";
  53. if (IsBuzz(i)) printNumber += "Buzz";
  54. if (IsNumber(printNumber))
  55. printNumber = (i).ToString(CultureInfo.InvariantCulture);
  56. resultFizzBuzz += " " + printNumber;
  57. }
  58. return resultFizzBuzz.Trim();
  59. }
  60. private static string GetNumbersUsingLinq(string resultFizzBuzz)
  61. {
  62. Enumerable.Range(1, 100)
  63. .Select(fb => String.Format("{0}{1}", fb % 3 == 0 ? "Fizz" : "", fb % 5 == 0 ? "Buzz" : ""))
  64. .Select(fb => fb != null ? (String.IsNullOrEmpty(fb) ? fb.ToString(CultureInfo.InvariantCulture) : fb) : null)
  65. .ToList()
  66. .ForEach(x => resultFizzBuzz = x);
  67. return resultFizzBuzz.Trim();
  68. }
  69. private static bool IsNumber(string printNumber)
  70. {
  71. return String.IsNullOrEmpty(printNumber);
  72. }
  73. private static bool IsBuzz(int i)
  74. {
  75. return i % 5 == 0;
  76. }
  77. private static bool IsFizz(int i)
  78. {
  79. return i % 3 == 0;
  80. }
  81. #endregion
  82. }
How to write TDD

Please note that here we are following Red Green and the Refactor approach of Test Driven Develoment.

The following are the tests:

Here are the complete tests:

  1. using System;
  2. using NUnit.Framework;
  3. namespace TDD_Katas_project.FizzBuzzKata
  4. {
  5. [TestFixture]
  6. [Category("TheFizzBuzzKata")]
  7. public class TestFizzBuzz
  8. {
  9. #region Private members
  10. private string _resultFizz;
  11. #endregion
  12. #region Setup/TearDown
  13. [TestFixtureSetUp]
  14. public void Setup()
  15. {
  16. _resultFizz = @"1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz 41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz Fizz 52 53 Fizz Buzz 56 Fizz 58 59 FizzBuzz 61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71 Fizz 73 74 FizzBuzz 76 77 Fizz 79 Buzz Fizz 82 83 Fizz Buzz 86 Fizz 88 89 FizzBuzz 91 92 Fizz 94 Buzz Fizz 97 98 Fizz Buzz";
  17. }
  18. [TestFixtureTearDown]
  19. public void TearDown()
  20. {
  21. _resultFizz = null;
  22. }
  23. #endregion
  24. #region Test Methods
  25. [Test]
  26. public void CanTestFizz()
  27. {
  28. Console.WriteLine(FizzBuzz.PrintFizzBuzz());
  29. Assert.That(FizzBuzz.PrintFizzBuzz(), Is.EqualTo(_resultFizz));
  30. }
  31. [Test]
  32. [TestCase(-1)]
  33. [TestCase(101)]
  34. [TestCase(0)]
  35. public void CanThrowArgumentExceptionWhenSuppliedNumberDoesNotMeetRule(int number)
  36. {
  37. var exception = Assert.Throws<ArgumentException>(() => FizzBuzz.PrintFizzBuzz(number));
  38. Assert.That(exception.Message, Is.EqualTo(string.Format("entered number is [{0}], which does not meet rule, entered number should be between 1 to 100.", number)));
  39. }
  40. [Test]
  41. [TestCase(1, "1")]
  42. [TestCase(3, "Fizz")]
  43. [TestCase(5, "Buzz")]
  44. [TestCase(15, "FizzBuzz")]
  45. [TestCase(30, "FizzBuzz")]
  46. public void CanTestSingleNumber(int number, string expectedresult)
  47. {
  48. var actualresult = FizzBuzz.PrintFizzBuzz(number);
  49. Assert.That(actualresult, Is.EqualTo(expectedresult),
  50. string.Format("result of entered number [{0}] is [{1}] but it should be [{2}]", number,
  51. actualresult, expectedresult));
  52. }
  53. #endregion
  54. }
  55. }
Please note that in the above we used the Nunit framework for TDD.

You can download the entire collection of TDDs from GitHub: https://github.com/garora/TDD-Katas.