Why day is day, night night, and time is time,
Were nothing but to waste night, day, and time.
Therefore, since brevity is the soul of wit,"
-- Shakespeare, Hamlet
Act II, Scene II, Lines 87-90
I'll admit it, sometimes I have no freaking clue what I am doing. Much like the internet dog meme, I feel completely out of my league. You have to start somewhere and no clue is often the first stop on the journey to mastery.
"A journey of a thousand leagues started with what was under one footstep."
-- Tao Te Ching verse 64, translated by Jan J. L. Duyvendak
Property Testing allows one to run their code under test through it's paces. Let us take a look at using Property Testing against FizzBuzz and see what we can learn.
We see that we have a fairly simple implementation of FizzBuzz.
public string Translate(int value){var result = string.Empty;if (value % 3 == 0) result += "Fizz";if (value % 5 == 0) result += "Buzz";return string.IsNullOrEmpty(result) ? value.ToString() : result;}
For our first test case we use the Test Case property of NUnit to show what the results of the Translate on the FizzBuzzer would be for different inputs. Note, I am showing the result here after many rounds of Red, Green, Refactor. I cannot predict the future and as such I had two different test methods for 2 and 3 and ended up refactoring them to the "one" you see below, but I digress.
[TestCase(2, Result = "2")][TestCase(3, Result = "Fizz")][TestCase(5, Result = "Buzz")][TestCase(15, Result = "FizzBuzz")]public string Given_Value_It_Must_Return_The_Given_Result(int value){return _fizzBuzzer.Translate(value);}
This is great, but what happens for other values? How do we know if this really works?
[Test]public void Given_A_Number_Not_Divisible_By_3_Or_5_It_Must_Return_That_Number(){var value =from number in Any.OfType<int>()where number%3 != 0 && number%5 != 0select number;Spec.For(value, v => _fizzBuzzer.Translate(v).Equals(v.ToString())).QuickCheckThrowOnFailure();}
We use the Property Test above using fscheck to show that any number not divisible by 3 or 5 will return the ToString value of the number. We do not have to check 15 since 3 * 5 =15 and therefore it is covered by the Fundamental Theory of Arithmetic.
private static Gen<int> DivisibleBy(int divisor){var divisibleBy =from number in Any.OfType<int>()where number % divisor == 0select number;return divisibleBy;}
[TestCase(3, "Fizz")]
[TestCase(5, "Buzz")][TestCase(15, "FizzBuzz")]public void Given_A_Number_Divisible_By_Divisor_It_Must_Contain_Expected(int divisor, string expected){Spec.For(DivisibleBy(divisor), d => _fizzBuzzer.Translate(d).Contains(expected)).QuickCheckThrowOnFailure();}
Next we test that every number divisible by 3 contains the string "Fizz", likewise we do the same with 5 and "Buzz" and 15 with "FizzBuzz". We check that they contain the string, so that if we get a value like 45, which is 3 * 15, for our divisible by 3 value we do not have a failing test because we got "FizzBuzz" back instead of just "Fizz", this is a very important thing to think about with Property Testing. Note also, this is another case were refactoring played a big part in the final result. I did not start off with a DivisibleBy function; no I found that the generate code for the 3 and 5 looked a lot a liked, so I combined them into the function you now see.
[Test]public void Given_A_Number_It_Must_Return_Fizz_Buzz_FizzBuzz_Or_A_Number(){Spec.ForAny<int>(x => true).Classify(x => _fizzBuzzer.Translate(x).Equals("Fizz"), "Fizz").Classify(x => _fizzBuzzer.Translate(x).Equals("Buzz"), "Buzz").Classify(x => _fizzBuzzer.Translate(x).Equals("FizzBuzz"), "FizzBuzz").Classify(x => Regex.IsMatch(_fizzBuzzer.Translate(x), @"\d+"), "number").QuickCheckThrowOnFailure();}
To show that the generator was in fact covering all four different possibilities I set up a "test" which was using fscheck's classification to show the break down of the different values which were coming out of Translate.
[Test]public void Given_A_Number_Divisible_By_3_And_5_It_Must_Contain_Both_Fizz_And_Buzz(){Spec.For(DivisibleBy(3*5), d => string.IsNullOrEmpty(_fizzBuzzer.Translate(d)) == false).And(d => _fizzBuzzer.Translate(d).Contains("Fizz")).And(d => _fizzBuzzer.Translate(d).Contains("Buzz")).QuickCheckThrowOnFailure();}
Last I set up a test case to show that if a value was divisible by both 3 and 5 it will contain both "Fizz" and "Buzz". Now this test case was not needed since it was covered above, but I wanted to show how the And works.
There you have it FizzBuzz using Property Testing. I found the examples in fscheck's GitHub repo very helpful.
Note, I used QuickCheckThrownOnFailure to cause the unit test to actually fail the test runner when the property is not true for some value. This is very important to do if you are using something like NCrunch to run your tests. If you have a property which fails for some value and do not use QuickCheckThrownOnFailure on your tests it will "pass" from the runner's point of view, but the result will give the value which falsifies it, this was not what I wanted so I had fscheck throw an exception when the property was falsified.