-- Charles Dickens, A Tale of Two Cities
As I've stated before, I like the FizzBuzz kata. FizzBuzz is one of those rare things that is very easy to understand and yet provides tons of depth once you really think about it.
Welcome to part two, last time we did FizzBuzz using TDD, this time we'll do it again but will use the Transformation Priority Premise to guide our TDD.
In TPP, Uncle Bob give us the following order for our transformations to our code:
The idea being that a transformation that is higher should always take place instead of one that is lower. If this idea is followed our code will be cleaner.
Let's apply the Transformation Priority Premise to FizzBuzz using Haskell and HUnit.
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
tests = TestList [TestLabel "3 is Fizz" t3]
[1 of 1] Compiling Main ( fizzBuzz.hs, interpreted )
fizzBuzz.hs:3:48: Not in scope: `fizzBuzzer'
Failed, modules loaded: none.
As I've stated before, I like the FizzBuzz kata. FizzBuzz is one of those rare things that is very easy to understand and yet provides tons of depth once you really think about it.
Welcome to part two, last time we did FizzBuzz using TDD, this time we'll do it again but will use the Transformation Priority Premise to guide our TDD.
In TPP, Uncle Bob give us the following order for our transformations to our code:
- ({}–>nil) no code at all->code that employs nil
- (nil->constant)
- (constant->constant+) a simple constant to a more complex constant
- (constant->scalar) replacing a constant with a variable or an argument
- (statement->statements) adding more unconditional statements.
- (unconditional->if) splitting the execution path
- (scalar->array)
- (array->container)
- (statement->recursion)
- (if->while)
- (expression->function) replacing an expression with a function or algorithm
- (variable->assignment) replacing the value of a variable.
The idea being that a transformation that is higher should always take place instead of one that is lower. If this idea is followed our code will be cleaner.
Let's apply the Transformation Priority Premise to FizzBuzz using Haskell and HUnit.
3 => "Fizz"
Failing Test Case
import Test.HUnitt3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
tests = TestList [TestLabel "3 is Fizz" t3]
[1 of 1] Compiling Main ( fizzBuzz.hs, interpreted )
fizzBuzz.hs:3:48: Not in scope: `fizzBuzzer'
Failed, modules loaded: none.
Pass
Using the following rules:
({}–>nil) no code at all->code that employs nil
(nil->constant)
fizzBuzzer::Int->String
fizzBuzzer n = "Fizz"
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
tests = TestList [TestLabel "3 is Fizz" t3
,TestLabel "5 is Buzz" t5]
### Failure in: 1:5 is Buzz
5 is Buzz
expected: "Buzz"
but got: "Fizz"
Cases: 2 Tried: 2 Errors: 0 Failures: 1
Counts {cases = 2, tried = 2, errors = 0, failures = 1}
*Main> runTestTT tests
import Test.HUnit
fizzBuzzer::Int->String
fizzBuzzer 3 = "Fizz"
fizzBuzzer 5 = "Buzz"
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
tests = TestList [TestLabel "3 is Fizz" t3
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25]
import Test.HUnit
fizzBuzzer::Int->String
fizzBuzzer 3 = "Fizz"
fizzBuzzer n = "Buzz"
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
tests = TestList [TestLabel "3 is Fizz" t3
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25]
Cases: 3 Tried: 2 Errors: 0 Failures: 0
Cases: 3 Tried: 3 Errors: 0 Failures: 0
Counts {cases = 3, tried = 3, errors = 0, failures = 0}
Cases: 4 Tried: 0 Errors: 0 Failures: 0
Cases: 4 Tried: 1 Errors: 0 Failures: 0
### Failure in: 1:9 is Fizz
9 is Fizz
expected: "Fizz"
but got: "Buzz"
Cases: 4 Tried: 2 Errors: 0 Failures: 1
Cases: 4 Tried: 3 Errors: 0 Failures: 1
Cases: 4 Tried: 4 Errors: 0 Failures: 1
Counts {cases = 4, tried = 4, errors = 0, failures = 1}
import Test.HUnit
fizzBuzzer::Int->String
fizzBuzzer n
| (==) (mod n 5) 0 = "Buzz"
| otherwise = "Fizz"
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t9 = TestCase (assertEqual "9 is Fizz" "Fizz" (fizzBuzzer 9))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
tests = TestList [TestLabel "3 is Fizz" t3
,TestLabel "9 is Fizz" t9
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25]
Cases: 4 Tried: 2 Errors: 0 Failures: 0
Cases: 4 Tried: 3 Errors: 0 Failures: 0
Cases: 4 Tried: 4 Errors: 0 Failures: 0
Counts {cases = 4, tried = 4, errors = 0, failures = 0}
fizzBuzzer::Int->String
fizzBuzzer n
| (==) (mod n 5) 0 = "Buzz"
| otherwise = "Fizz"
t2 = TestCase (assertEqual "2 is 2" "2" (fizzBuzzer 2))
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t9 = TestCase (assertEqual "9 is Fizz" "Fizz" (fizzBuzzer 9))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
tests = TestList [TestLabel "2 is 2" t2
,TestLabel "3 is Fizz" t3
,TestLabel "9 is Fizz" t9
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25]
### Failure in: 0:2 is 2
2 is 2
expected: "2"
but got: "Fizz"
Cases: 5 Tried: 1 Errors: 0 Failures: 1
Cases: 5 Tried: 2 Errors: 0 Failures: 1
Cases: 5 Tried: 3 Errors: 0 Failures: 1
Cases: 5 Tried: 4 Errors: 0 Failures: 1
Cases: 5 Tried: 5 Errors: 0 Failures: 1
Counts {cases = 5, tried = 5, errors = 0, failures = 1}
(unconditional->if) splitting the execution path
import Test.HUnit
fizzBuzzer::Int->String
fizzBuzzer n
| (==) (mod n 5) 0 = "Buzz"
| (==) (mod n 3) 0 = "Fizz"
| otherwise = show n
t2 = TestCase (assertEqual "2 is 2" "2" (fizzBuzzer 2))
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t9 = TestCase (assertEqual "9 is Fizz" "Fizz" (fizzBuzzer 9))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
tests = TestList [TestLabel "2 is 2" t2
,TestLabel "3 is Fizz" t3
,TestLabel "9 is Fizz" t9
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25]
Cases: 5 Tried: 0 Errors: 0 Failures: 0
Cases: 5 Tried: 1 Errors: 0 Failures: 0
Cases: 5 Tried: 2 Errors: 0 Failures: 0
Cases: 5 Tried: 3 Errors: 0 Failures: 0
Cases: 5 Tried: 4 Errors: 0 Failures: 0
Cases: 5 Tried: 5 Errors: 0 Failures: 0
Counts {cases = 5, tried = 5, errors = 0, failures = 0}
(constant->constant+) a simple constant to a more complex constant
import Test.HUnit
xzzer n = (\x -> (==) (mod x n) 0)
fizzBuzzer::Int->String
fizzBuzzer n
| xzzer 3 = "Fizz"
| xzzer 5 = "Buzz"
| otherwise = show n
t2 = TestCase (assertEqual "2 is 2" "2" (fizzBuzzer 2))
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t9 = TestCase (assertEqual "9 is Fizz" "Fizz" (fizzBuzzer 9))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
tests = TestList [TestLabel "2 is 2" t2
,TestLabel "3 is Fizz" t3
,TestLabel "9 is Fizz" t9
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25]
Cases: 5 Tried: 0 Errors: 0 Failures: 0
Cases: 5 Tried: 1 Errors: 0 Failures: 0
Cases: 5 Tried: 2 Errors: 0 Failures: 0
Cases: 5 Tried: 3 Errors: 0 Failures: 0
Cases: 5 Tried: 4 Errors: 0 Failures: 0
Cases: 5 Tried: 5 Errors: 0 Failures: 0
Counts {cases = 5, tried = 5, errors = 0, failures = 0}
xzzer n = (\x -> (==) (mod x n) 0)
fizzBuzzer::Int->String
fizzBuzzer n
| xzzer 3 = "Fizz"
| xzzer 5 = "Buzz"
| otherwise = show n
t2 = TestCase (assertEqual "2 is 2" "2" (fizzBuzzer 2))
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t9 = TestCase (assertEqual "9 is Fizz" "Fizz" (fizzBuzzer 9))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
t15 = TestCase (assertEqual "15 is FizzBuzz" "FizzBuzz" (fizzBuzzer 15))
tests = TestList [TestLabel "2 is 2" t2
,TestLabel "3 is Fizz" t3
,TestLabel "9 is Fizz" t9
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25
,TestLabel "15 is FizzBuzz" t15]
Prelude> :reload
[1 of 1] Compiling Main ( fizzBuzz.hs, interpreted )
fizzBuzz.hs:7:5:
Couldn't match expected type `Bool' with actual type `a0 -> Bool'
In the return type of a call of `xzzer'
In the expression: xzzer 3
In a stmt of a pattern guard for
an equation for `fizzBuzzer':
xzzer 3
Failed, modules loaded: none.
import Test.HUnit
fizzBuzzer::Int->String
fizzBuzzer n
| result == "" = show n
| otherwise = result
where
xzzer n = (\x -> (==) (mod x n) 0)
fizzer x = if xzzer 3 x then "Fizz" else ""
buzzer x = if xzzer 5 x then "Buzz" else ""
result = foldl (\acc x -> acc ++ x n) "" [fizzer, buzzer]
t2 = TestCase (assertEqual "2 is 2" "2" (fizzBuzzer 2))
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t9 = TestCase (assertEqual "9 is Fizz" "Fizz" (fizzBuzzer 9))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
t15 = TestCase (assertEqual "15 is FizzBuzz" "FizzBuzz" (fizzBuzzer 15))
tests = TestList [TestLabel "2 is 2" t2
,TestLabel "3 is Fizz" t3
,TestLabel "9 is Fizz" t9
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25
,TestLabel "15 is FizzBuzz" t15]
Cases: 6 Tried: 2 Errors: 0 Failures: 0
Cases: 6 Tried: 3 Errors: 0 Failures: 0
Cases: 6 Tried: 4 Errors: 0 Failures: 0
Cases: 6 Tried: 5 Errors: 0 Failures: 0
Cases: 6 Tried: 6 Errors: 0 Failures: 0
Counts {cases = 6, tried = 6, errors = 0, failures = 0}
import Test.HUnit
fizzBuzzer::Int->String
fizzBuzzer n
| result == "" = show n
| otherwise = result
where
xzzer n = (\x -> (==) (mod x n) 0)
fizzer x = if xzzer 3 x then "Fizz" else ""
buzzer x = if xzzer 5 x then "Buzz" else ""
result = foldl (\acc x -> acc ++ x n) "" [fizzer, buzzer]
t2 = TestCase (assertEqual "2 is 2" "2" (fizzBuzzer 2))
t4 = TestCase (assertEqual "4 is 4" "4" (fizzBuzzer 4))
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t9 = TestCase (assertEqual "9 is Fizz" "Fizz" (fizzBuzzer 9))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
t15 = TestCase (assertEqual "15 is FizzBuzz" "FizzBuzz" (fizzBuzzer 15))
t30 = TestCase (assertEqual "30 is FizzBuzz" "FizzBuzz" (fizzBuzzer 30))
tests = TestList [TestLabel "2 is 2" t2
,TestLabel "4 is 4" t4
,TestLabel "3 is Fizz" t3
,TestLabel "9 is Fizz" t9
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25
,TestLabel "15 is FizzBuzz" t15
,TestLabel "30 is FizzBuzz" t30]
Cases: 8 Tried: 0 Errors: 0 Failures: 0
Cases: 8 Tried: 1 Errors: 0 Failures: 0
Cases: 8 Tried: 2 Errors: 0 Failures: 0
Cases: 8 Tried: 3 Errors: 0 Failures: 0
Cases: 8 Tried: 4 Errors: 0 Failures: 0
Cases: 8 Tried: 5 Errors: 0 Failures: 0
Cases: 8 Tried: 6 Errors: 0 Failures: 0
Cases: 8 Tried: 7 Errors: 0 Failures: 0
Cases: 8 Tried: 8 Errors: 0 Failures: 0
Counts {cases = 8, tried = 8, errors = 0, failures = 0}
(nil->constant)
import Test.HUnit
fizzBuzzer::Int->String
fizzBuzzer n = "Fizz"
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
tests = TestList [TestLabel "3 is Fizz" t3]
*Main> runTestTT tests
Cases: 1 Tried: 0 Errors: 0 Failures: 0
Cases: 1 Tried: 1 Errors: 0 Failures: 0
Counts {cases = 1, tried = 1, errors = 1, failures = 0}
Counts {cases = 1, tried = 1, errors = 1, failures = 0}
5 => "Buzz"
Failing Test Case
import Test.HUnitfizzBuzzer::Int->String
fizzBuzzer n = "Fizz"
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
tests = TestList [TestLabel "3 is Fizz" t3
,TestLabel "5 is Buzz" t5]
*Main> runTestTT tests
Cases: 2 Tried: 0 Errors: 0 Failures: 0
Cases: 2 Tried: 1 Errors: 0 Failures: 0
### Failure in: 1:5 is Buzz
5 is Buzz
expected: "Buzz"
but got: "Fizz"
Cases: 2 Tried: 2 Errors: 0 Failures: 1
Counts {cases = 2, tried = 2, errors = 0, failures = 1}
Pass
Using the following rules:
(constant->constant+) a simple constant to a more complex constant
import Test.HUnit
fizzBuzzer::Int->String
fizzBuzzer 3 = "Fizz"
fizzBuzzer 5 = "Buzz"
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
tests = TestList [TestLabel "3 is Fizz" t3
,TestLabel "5 is Buzz" t5]
*Main> runTestTT tests
Cases: 2 Tried: 0 Errors: 0 Failures: 0
Cases: 2 Tried: 1 Errors: 0 Failures: 0
Cases: 2 Tried: 2 Errors: 0 Failures: 0
Counts {cases = 2, tried = 2, errors = 0, failures = 0}
Cases: 2 Tried: 2 Errors: 0 Failures: 0
Counts {cases = 2, tried = 2, errors = 0, failures = 0}
25 => "Buzz"
fizzBuzzer::Int->String
fizzBuzzer 3 = "Fizz"
fizzBuzzer 5 = "Buzz"
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
tests = TestList [TestLabel "3 is Fizz" t3
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25]
*Main> runTestTT tests
Cases: 3 Tried: 0 Errors: 0 Failures: 0
Cases: 3 Tried: 1 Errors: 0 Failures: 0
Cases: 3 Tried: 2 Errors: 0 Failures: 0
Cases: 3 Tried: 2 Errors: 0 Failures: 0
### Error in: 2:25 is Buzz
fizzBuzz.hs:(4,1)-(5,21): Non-exhaustive patterns in function Main.fizzBuzzer
Cases: 3 Tried: 3 Errors: 1 Failures: 0
Counts {cases = 3, tried = 3, errors = 1, failures = 0}
Pass
Using the following rules:
(constant->scalar) replacing a constant with a variable or an argumentimport Test.HUnit
fizzBuzzer::Int->String
fizzBuzzer 3 = "Fizz"
fizzBuzzer n = "Buzz"
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
tests = TestList [TestLabel "3 is Fizz" t3
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25]
*Main> runTestTT tests
Cases: 3 Tried: 0 Errors: 0 Failures: 0
Cases: 3 Tried: 1 Errors: 0 Failures: 0Cases: 3 Tried: 2 Errors: 0 Failures: 0
Cases: 3 Tried: 3 Errors: 0 Failures: 0
Counts {cases = 3, tried = 3, errors = 0, failures = 0}
9 => "Fizz"
Failing Test Case
import Test.HUnit
fizzBuzzer::Int->String
fizzBuzzer 3 = "Fizz"
fizzBuzzer n = "Buzz"
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t9 = TestCase (assertEqual "9 is Fizz" "Fizz" (fizzBuzzer 9))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
tests = TestList [TestLabel "3 is Fizz" t3
,TestLabel "9 is Fizz" t9
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25]
*Main> runTestTT tests
Cases: 4 Tried: 1 Errors: 0 Failures: 0
### Failure in: 1:9 is Fizz
9 is Fizz
expected: "Fizz"
but got: "Buzz"
Cases: 4 Tried: 2 Errors: 0 Failures: 1
Cases: 4 Tried: 3 Errors: 0 Failures: 1
Cases: 4 Tried: 4 Errors: 0 Failures: 1
Counts {cases = 4, tried = 4, errors = 0, failures = 1}
Pass
Using the following rules:
(unconditional->if) splitting the execution pathimport Test.HUnit
fizzBuzzer::Int->String
fizzBuzzer n
| (==) (mod n 5) 0 = "Buzz"
| otherwise = "Fizz"
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t9 = TestCase (assertEqual "9 is Fizz" "Fizz" (fizzBuzzer 9))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
tests = TestList [TestLabel "3 is Fizz" t3
,TestLabel "9 is Fizz" t9
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25]
*Main> runTestTT tests
Cases: 4 Tried: 0 Errors: 0 Failures: 0
Cases: 4 Tried: 1 Errors: 0 Failures: 0Cases: 4 Tried: 2 Errors: 0 Failures: 0
Cases: 4 Tried: 3 Errors: 0 Failures: 0
Cases: 4 Tried: 4 Errors: 0 Failures: 0
Counts {cases = 4, tried = 4, errors = 0, failures = 0}
2 => "2"
Failing Test Case
import Test.HUnitfizzBuzzer::Int->String
fizzBuzzer n
| (==) (mod n 5) 0 = "Buzz"
| otherwise = "Fizz"
t2 = TestCase (assertEqual "2 is 2" "2" (fizzBuzzer 2))
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t9 = TestCase (assertEqual "9 is Fizz" "Fizz" (fizzBuzzer 9))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
tests = TestList [TestLabel "2 is 2" t2
,TestLabel "3 is Fizz" t3
,TestLabel "9 is Fizz" t9
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25]
*Main> runTestTT tests
Cases: 5 Tried: 0 Errors: 0 Failures: 0
### Failure in: 0:2 is 2
2 is 2
expected: "2"
but got: "Fizz"
Cases: 5 Tried: 1 Errors: 0 Failures: 1
Cases: 5 Tried: 2 Errors: 0 Failures: 1
Cases: 5 Tried: 3 Errors: 0 Failures: 1
Cases: 5 Tried: 4 Errors: 0 Failures: 1
Cases: 5 Tried: 5 Errors: 0 Failures: 1
Counts {cases = 5, tried = 5, errors = 0, failures = 1}
Pass
Using the following rules:
(constant->constant+) a simple constant to a more complex constant(unconditional->if) splitting the execution path
import Test.HUnit
fizzBuzzer::Int->String
fizzBuzzer n
| (==) (mod n 5) 0 = "Buzz"
| (==) (mod n 3) 0 = "Fizz"
| otherwise = show n
t2 = TestCase (assertEqual "2 is 2" "2" (fizzBuzzer 2))
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t9 = TestCase (assertEqual "9 is Fizz" "Fizz" (fizzBuzzer 9))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
tests = TestList [TestLabel "2 is 2" t2
,TestLabel "3 is Fizz" t3
,TestLabel "9 is Fizz" t9
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25]
*Main> runTestTT tests
Cases: 5 Tried: 1 Errors: 0 Failures: 0
Cases: 5 Tried: 2 Errors: 0 Failures: 0
Cases: 5 Tried: 3 Errors: 0 Failures: 0
Cases: 5 Tried: 4 Errors: 0 Failures: 0
Cases: 5 Tried: 5 Errors: 0 Failures: 0
Counts {cases = 5, tried = 5, errors = 0, failures = 0}
Refactor
We now have a repeating pattern, let's refactor it our using the following rule:(constant->constant+) a simple constant to a more complex constant
import Test.HUnit
xzzer n = (\x -> (==) (mod x n) 0)
fizzBuzzer::Int->String
fizzBuzzer n
| xzzer 3 = "Fizz"
| xzzer 5 = "Buzz"
| otherwise = show n
t2 = TestCase (assertEqual "2 is 2" "2" (fizzBuzzer 2))
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t9 = TestCase (assertEqual "9 is Fizz" "Fizz" (fizzBuzzer 9))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
tests = TestList [TestLabel "2 is 2" t2
,TestLabel "3 is Fizz" t3
,TestLabel "9 is Fizz" t9
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25]
*Main> runTestTT tests
Cases: 5 Tried: 1 Errors: 0 Failures: 0
Cases: 5 Tried: 2 Errors: 0 Failures: 0
Cases: 5 Tried: 3 Errors: 0 Failures: 0
Cases: 5 Tried: 4 Errors: 0 Failures: 0
Cases: 5 Tried: 5 Errors: 0 Failures: 0
Counts {cases = 5, tried = 5, errors = 0, failures = 0}
15 => "FizzBuzz"
Failing Test Case
import Test.HUnitxzzer n = (\x -> (==) (mod x n) 0)
fizzBuzzer::Int->String
fizzBuzzer n
| xzzer 3 = "Fizz"
| xzzer 5 = "Buzz"
| otherwise = show n
t2 = TestCase (assertEqual "2 is 2" "2" (fizzBuzzer 2))
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t9 = TestCase (assertEqual "9 is Fizz" "Fizz" (fizzBuzzer 9))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
t15 = TestCase (assertEqual "15 is FizzBuzz" "FizzBuzz" (fizzBuzzer 15))
tests = TestList [TestLabel "2 is 2" t2
,TestLabel "3 is Fizz" t3
,TestLabel "9 is Fizz" t9
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25
,TestLabel "15 is FizzBuzz" t15]
Prelude> :reload
[1 of 1] Compiling Main ( fizzBuzz.hs, interpreted )
fizzBuzz.hs:7:5:
Couldn't match expected type `Bool' with actual type `a0 -> Bool'
In the return type of a call of `xzzer'
In the expression: xzzer 3
In a stmt of a pattern guard for
an equation for `fizzBuzzer':
xzzer 3
Failed, modules loaded: none.
Pass
Using the following rules:
(constant->constant+) a simple constant to a more complex constantimport Test.HUnit
fizzBuzzer::Int->String
fizzBuzzer n
| result == "" = show n
| otherwise = result
where
xzzer n = (\x -> (==) (mod x n) 0)
fizzer x = if xzzer 3 x then "Fizz" else ""
buzzer x = if xzzer 5 x then "Buzz" else ""
result = foldl (\acc x -> acc ++ x n) "" [fizzer, buzzer]
t2 = TestCase (assertEqual "2 is 2" "2" (fizzBuzzer 2))
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t9 = TestCase (assertEqual "9 is Fizz" "Fizz" (fizzBuzzer 9))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
t15 = TestCase (assertEqual "15 is FizzBuzz" "FizzBuzz" (fizzBuzzer 15))
tests = TestList [TestLabel "2 is 2" t2
,TestLabel "3 is Fizz" t3
,TestLabel "9 is Fizz" t9
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25
,TestLabel "15 is FizzBuzz" t15]
*Main> runTestTT tests
Cases: 6 Tried: 0 Errors: 0 Failures: 0
Cases: 6 Tried: 1 Errors: 0 Failures: 0Cases: 6 Tried: 2 Errors: 0 Failures: 0
Cases: 6 Tried: 3 Errors: 0 Failures: 0
Cases: 6 Tried: 4 Errors: 0 Failures: 0
Cases: 6 Tried: 5 Errors: 0 Failures: 0
Cases: 6 Tried: 6 Errors: 0 Failures: 0
Counts {cases = 6, tried = 6, errors = 0, failures = 0}
More
Let's add in a few more test cases just to show we have all the bases covered.import Test.HUnit
fizzBuzzer::Int->String
fizzBuzzer n
| result == "" = show n
| otherwise = result
where
xzzer n = (\x -> (==) (mod x n) 0)
fizzer x = if xzzer 3 x then "Fizz" else ""
buzzer x = if xzzer 5 x then "Buzz" else ""
result = foldl (\acc x -> acc ++ x n) "" [fizzer, buzzer]
t2 = TestCase (assertEqual "2 is 2" "2" (fizzBuzzer 2))
t4 = TestCase (assertEqual "4 is 4" "4" (fizzBuzzer 4))
t3 = TestCase (assertEqual "3 is Fizz" "Fizz" (fizzBuzzer 3))
t9 = TestCase (assertEqual "9 is Fizz" "Fizz" (fizzBuzzer 9))
t5 = TestCase (assertEqual "5 is Buzz" "Buzz" (fizzBuzzer 5))
t25 = TestCase (assertEqual "25 is Buzz" "Buzz" (fizzBuzzer 25))
t15 = TestCase (assertEqual "15 is FizzBuzz" "FizzBuzz" (fizzBuzzer 15))
t30 = TestCase (assertEqual "30 is FizzBuzz" "FizzBuzz" (fizzBuzzer 30))
tests = TestList [TestLabel "2 is 2" t2
,TestLabel "4 is 4" t4
,TestLabel "3 is Fizz" t3
,TestLabel "9 is Fizz" t9
,TestLabel "5 is Buzz" t5
,TestLabel "25 is Buzz" t25
,TestLabel "15 is FizzBuzz" t15
,TestLabel "30 is FizzBuzz" t30]
*Main> runTestTT tests
Cases: 8 Tried: 1 Errors: 0 Failures: 0
Cases: 8 Tried: 2 Errors: 0 Failures: 0
Cases: 8 Tried: 3 Errors: 0 Failures: 0
Cases: 8 Tried: 4 Errors: 0 Failures: 0
Cases: 8 Tried: 5 Errors: 0 Failures: 0
Cases: 8 Tried: 6 Errors: 0 Failures: 0
Cases: 8 Tried: 7 Errors: 0 Failures: 0
Cases: 8 Tried: 8 Errors: 0 Failures: 0
Counts {cases = 8, tried = 8, errors = 0, failures = 0}
Conclusion
We now have a FizzBuzz that we can show to our mom, assuming that our mom knows how to program and understands Haskell (my mom does not hit into either of those categories). I am sure someone with more experience in TPP and Haskell could do better, but I believe this is fairly good.