Red, Green, Refactor!

Tun Khine
7 min readOct 26, 2019

--

Robert Cecil Martin, better know as Uncle Bob describes Test Driven Development (TDD) in terms of three simple rules.

  1. You are not allowed to write any production code unless it is to make a failing unit test pass.
  2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
  3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test.

Running tests give you fast confirmation of whether your code behaves as it should or not. Some devs might believe that automated testing increases the amount of development time. However, it’s been proven that automated tests actually allow devs and their team to develop projects much quicker and with a higher quality. TDD has many benefits such as simpler designs, more test coverage, and improved code quality. It provides a structure for developers to operate within that can often yield high coding standards. If a team adopts TDD then all developers write tests for the code they write.

In this week’s article I’ll cover writing tests with the popular end-to-end testing platform, Cypress.io, and the notorious developer interview test…FizzBuzz!

Setup

Let’s begin by initializing a new project in your terminal, cd-ing into its directory, installing Cypress as a dependency, open Cypress and finally open your favorite code editor.

npx create-react-app fizzbuzztdd
cd fizzbuzztdd
npm install cypress
npx cypress open
code .

If you’re opening up Cypress for the first time it’ll give you the below prompt showing you the folders and files added to your project. They even throw in a suite of example tests to help you get started.

Let’s create our own test file in./cypress/integration called fizzbuzz.spec.js. When the file is created, Cypress will pick it up and it will be shown in under Integration Tests.

Go ahead and click on the test and a new window should open up. Now that we’ve got that out of the way, let’s tackle FizzBuzz and writing our first unit tests!

What is FizzBuzz??

FizzBuzz is a very simple programming task, used in software developer job interviews, to determine whether the job candidate can actually write code. It was invented by Imran Ghory, and popularized by Jeff Atwood. Here is a description of the tasks:

  • Create a function that takes a value and returns that value as a string
  • For multiples of three print “Fizz”, instead of the value
  • For the multiples of five print “Buzz”, instead of the value.
  • For numbers which are multiples of both three and five print “FizzBuzz”.

It’s very well known in software development circles. There are multiple implementations in every language, joke implementations, and plenty of articles discussing its usefulness during the hiring process.

Red, Green, Refactor

Now that we understand what FizzBuzz is, let’s get to writing a test that first fails, then write the code to get it to pass, refactor that code, and finally starting the process over again.

Unit tests are made up of a describe block, an it function that describes what our test does, and finally an assertion or expect. Using those attributes, our first test in fizzbuzz.spec.js could look something like this:

Once we save that spec file and take a look in our testing window we already notice our first failing test!

Naturally it gives throws us an error that indicates the function FizzBuzz is undefined. Let’s proceed to write the code that’ll get our first test green.

Congrats! You just wrote your first passing test that satisfies the first requirement in FizzBuzz. Can you think of anything that we can refactor here? I believe we’re in a good place to start the process of writing another test that fails.

For the next deliverable we want our FizzBuzz function to print “Fizz” for any multiples of the number 3. The test could look something like this:

We could easily get this test to pass by slightly modifying our function to include an if statement like so:

function fizzbuzz(value) {
if(value === 3) return "Fizz";
else return value.toString();
}

Unfortunately this doesn’t quite satisfy our deliverable as it only works for a value of three, rather than any multiples of three. Let’s refactor to get this clean and concise like so:

function fizzbuzz(value) {
if(value % 3 == 0) return "Fizz";
else return value.toString();
}

You’ll see that we used the modulus operator to return a remainder after you divide something. In this case the if statement says, if the value divided by 3 has a remainder of 0, return “Fizz”. Otherwise convert the value to a string. If you go back and check the test window, you’ll notice that you still have green! We could strengthen our test by chaining a few more expect assertions on the end of our test to make sure the function truly satisfies our requirement like so:

Great! Now that we have this test passing and refactored, we can move on to the next deliverable. // For the multiples of five print “Buzz”.

Our next failing test will look similar to our last, and with some additional assertions, might look something like :

To get this test passing, we could certainly add another if statement into our FizzBuzz function like so:

function fizzbuzz(value) {
if(value % 3 === 0) return "Fizz";
else if(value % 5 === 0) return "Buzz";
else return value.toString()
}

Getting those tests green is getting really addicting, isn’t it? Let’s move onto our next deliverable, // For numbers which are multiples of both three and five print “FizzBuzz”.

Simple enough right? We could just throw another else if statement that says (value % 3 === 0 && value % 5 === 0 ) return “FizzBuzz;”

function fizzbuzz(value) {
if(value % 3 == 0) return "Fizz";
else if(value % 5 == 0) return "Buzz";
else if (value % 3 === 0 && value % 5 === 0 ) return “FizzBuzz”;
else return value.toString()
}

Wait what? Why isn’t our last test passing?? I’m sure I wrote it correctly…Simply put it’s reading our first two conditionals and thus never making it’s way down to the third. Lets go ahead and change the order of our statements around like so:

function fizzbuzz(value) {
if (value % 3 === 0 && value % 5 === 0 ) return “FizzBuzz”;
else if(value % 3 == 0) return "Fizz";
else if(value % 5 == 0) return "Buzz";
else return value.toString()
}

Huzzah! All our tests are passing!! Let’s think about how we could refactor our function and make it easier to read.

I might do something like creating easy to read variable names:

function fizzbuzz(value) {
const multipleOfThree = value % 3 === 0;
const multipleOfFive = value % 5 === 0;
if (multipleOfThree && multipleOfFive) return "FizzBuzz";
else if (multipleOfThree) return "Fizz";
else if (multipleOfFive) return "Buzz";
else return value.toString();
}

Conclusion

In this article, you’ve worked through an example of how TDD improves confidence in the code YOU write, ensuring everything is working as expected. You’ve also used existing tests to stay on track while you confidently refactor implementation code.

Writing unit tests the right way is a skill you usually do not learn at bootcamps or in normal programming education. It’s certainly hard the few first times, and like anything else, requires practice before seeing the results. I encourage you and myself to keep at it!

This build can be found on Github here.

--

--

Tun Khine

Software Engineer, former International Civil Servant, Dad, and new ATLien