Introducing FizzBuzz
When we interview programmers who will write code to help with testing, we like the exercise FizzBuzz. The exercise requires the programmer to understand conditionals (which are if
statements), looping, and the modulus operator (which is the remainder in division). Let’s see what a typical assignment might look like.
In the children’s game of Fizzbuzz, players rotate, keeping a count that starts with one. If the next number is divisible by three, players say “Fizz.” If it is divisible by five, they say “Buzz.” If it is not divisible, they say the number. The goal is to write a computer program that runs on the command line and takes in the number to count up to in FizzBuzz math, then puts the output on the screen.
Matt wrote up an implementation of FizzBuzz in Ruby. Instead of the most powerful constructs of the language, he used the ones that were easiest to read. The following output could be “tighter,” by far, but it was designed to be readable by nearly all audiences. The examples for the rest of this book will try to match this style. For a book with a more powerful Ruby style that also includes tests, we like Poodr, or Practical Object Oriented Programming In Ruby, by Sandi Metz. That book also has good coverage of testing concepts. If you aren’t a Rubyist, our GitHub repository contains examples in Python.
GitHub
For now, here’s the heart of the code; the entire program is available on GitHub at https://github.com/PacktPublishing/Software-Testing-Strategies/blob/main/Chapter03/ruby/FizzBuzz01.rb.
This code takes a command-line argument for how high to count, checks to make sure that it is valid, and then spits out numbers. The heart of the program is nine lines of code:
count_to = ARGV[0].to_i; if !defined?(count_to) or count_to<1 puts "Use: FizzBuzz01.rb (count)\n" puts "Where count is a round number of value one or higher" abort(""); end for count in 1..count_to do if count % 3 == 0 puts "Fizz" elsif count % 5 == 0 puts "Buzz" else puts count.to_s(); end end
It’s worth noting that Matt created four syntax errors just by re-typing the code into this book. Here’s what happens when it runs on a Mac, selecting an output from 1 to 12:
Figure 3.4 – Fizzbuzz output from Matt’s laptop
If the four errors just to write a dozen lines of code doesn’t give you pause, the program has a significant defect in it. Can you see it?
You won’t see it in the output. I’ll wait. Go read the code.
Okay. Here’s the problem.
The numbers 15, 30, 45, and 60 are all divisible by both three and five. The preceding program will divide 15 by 3, decide it is “Fizz”, and exit. We need something more robust.
The obvious problem with FizzBuzz is that it has problems. The programmer will have to set it up, run it, and check the output. If they find another problem, the programmer will need to make another change and run it again. This change-run-check dynamic is sometimes called debugging. It is the only way to run the program because the business logic to calculate the results, the input, and the printing are all combined into one “unit.”
At the time of writing, the only way to test this is from the customer, or user, perspective. That isn’t the end of the world as it is only nine lines of code – but as programs get larger, debugging becomes more and more painful. It would be better to compose our software of smaller, independent units that work a much larger percentage of the time. That’s where unit testing comes in – but for it to work, we need to create code libraries that can be independently tested.
If a program can’t be independently tested, separate from the user interface, some people call that untestable. We don’t go that far as our hands and fingers seem to work just fine.
Still, unit tests allow us to get into the program so that we can define how it should work. They work as living documentation for the expectations of each component. As discussed earlier, a program that has 20 units that are 95% reliable, for some definition of reliable, is going to be massively more reliable than one where the units are 70% reliable.
Fizzbuzz needs unit tests.
So, let’s define a unit, unit testing, and fix this thing.