Learning TDD with fizzbuzz

Testing JavaScript is something all front-end developers should be doing, although for those like myself who come from a design background rather than from programming, it can be difficult to know where to start.

Why TDD?

There are a number of testing methodologies. My preferred is method is test-driven development, also known as TDD. The TDD works is that you write your tests and see them fail. You then write enough code to make them pass and refactor accordingly.

There are a number of benefits to this approach, such as having confidence that any changes you make aren’t going to break something else. It also acts as an outline of the requirements of your application.

Getting started

In order to learn about JavaScript testing, you need something simple to get you going. What worked for me was the fizzbuzz kata.

The rules of fizzbuzz are:

  • When passed an argument that is divisible by three, the function should return the string ‘fizz’.
  • When passed an argument that is divisible by five, the function should return the string ‘buzz’.
  • When passed an argument that is divisible by three and five, the function should return the string ‘fizzbuzz’.
  • When passed an argument that is not divisible by three or five, the function should return the argument.

Setting up

The first thing you need is a directory to keep all your files in. You can call this anything you want. For simplicity I’ve called it fizzbuzz.

Next you’ll need a testing framework. For this tutorial we are going to use Jasmine. Download the latest standalone release of Jasmine.

Delete everything in the spec and src directories. These are just examples that we won’t be using. Open SpecRunner.html in your editor. You should see a section resembling this:

1
2
3
4
5
6
7
<!-- include source files here... -->
<script type="text/javascript" src="src/Player.js"></script>
<script type="text/javascript" src="src/Song.js"></script>

<!-- include spec files here... -->
<script type="text/javascript" src="spec/SpecHelper.js"></script>
<script type="text/javascript" src="spec/PlayerSpec.js"></script>

Change this to the following, and then create the corresponding files:

1
2
3
4
5
<!-- include source files here... -->
<script type="text/javascript" src="src/fizzbuzz.js"></script>

<!-- include spec files here... -->
<script type="text/javascript" src="spec/fizzbuzzSpec.js"></script>

Going forward, we will be writing our tests in spec/fizzbuzzSpec.js and writing our code in src/fizzbuzz.js.

Running your tests

Running your tests is easy. All you have to do is open SpecRunner.html in your browser. This will tell you if your tests are passing or which ones have failed, and why.

Right now you should see a message that says:

No specs found.

This is to be expected, as we haven’t written any tests yet.

Setting up your test suite

First we’ll describe our test suite. Here it makes sense to use the name of the method you are testing. Add the following to spec/fizzbuzzSpec.js:

1
2
3
describe(fizzbuzz, function () {
  // Tests will go here
});

All of our tests for this method will be wrapped in this function. Now reload SpecRunner.html in your browser. You will still see the ‘No specs found’ message, but you should also see the name of your test suite, which in this case in fizzbuzz.

Writing your first test

Your tests should be based around the requirements for the application you are building. Let’s revisit the requirements above:

When passed an argument that is divisible by three, the function should return the string ‘fizz’.

The syntax for writing a test in Jasmine is straightforward. We nest our test inside an it function, with the first argument being the description of what our test expects. Inside that we use the expect method to pass in our fizzbuzz function and use the toEqual matcher to tell it what to expect:

1
2
3
it(should return fizz when arg is divisible by three, function () {
  expect(fizzbuzz(3)).toEqual(fizz);
});

Now if we reload specRunner.html we can see a failing test. In the red section we can see which test has failed, and below we can see what the error is:

ReferenceError: fizzbuzz is not defined

Once again, this isn’t surprising as we haven’t written any code yet so fizzbuzz doesn’t exist. Let’s write some code to fix that.

In src/fizzbuzz.js write define your fizzbuzz function:

1
2
function fizzbuzz() {
}

This time when we reload specRunner.html we still see a failing test, but the error is different:

Expected undefined to equal ‘fizz’.

As fizzbuzz isn’t returning anything, we are getting undefined where our test is expecting the string fizz. So let’s write our first condition inside fizzbuzz:

1
2
3
4
5
function fizzbuzz(n) {
  if (n % 3 === 0) {
    return fizz;
  }
}

Now your test will pass. You should see a message in the green section that says:

1 spec, 0 failures

Filling out your test suite

Now we’ve got our first test passing, let’s move on to the next requirement:

When passed an argument that is divisible by five, the function should return the string ‘buzz’.

The test will be similar to the previous test, but the argument we pass into our fizzbuzz function will have to change to match the requirement:

1
2
3
it (should return buzz when arg is divisible by five, function () {
  expect(fizzbuzz(5)).toEqual(buzz);
});

Reload SpecRunner.html again. We’re not expecting this to pass yet as we haven’t accounted for this condition in our fizzbuzz function. To make this test pass we’ll need to add the following to fizzbuzz:

1
2
3
4
5
6
7
function fizzbuzz(n) {
  if (n % 3 === 0) {
    return fizz;
  } else if (n % 5 === 0) {
    return buzz;
  }
}

Reload specRunner.html again and this test will pass.

Hopefully by now you’re starting to see how this works. We have two more tests to write to cover our requirements, so for completeness sake we’ll whizz through those and the code needed to make them pass.

Our next requirement:

When passed an argument that is divisible by three and five, the function should return the string ‘fizzbuzz’.

The test for this requirement:

1
2
3
it(should return fizzbuzz when arg is divisible by three and five, function () {
  expect(fizzbuzz(15)).toEqual(fizzbuzz);
});

The code to make that pass:

1
2
3
4
5
6
7
8
9
10
11
function fizzbuzz(n) {
  if (n % 3 === 0) {
    if (n % 5 === 0) {
      return fizzbuzz;
    } else {
      return fizz;
    }
  } else if (n % 5 === 0) {
    return buzz;
  }
}

Our final requirement:

When passed an argument that is not divisible by three or five, the function should return the argument.

The test for this requirement:

1
2
3
it(should return the arg if not divisible by three or five, function () {
  expect(fizzbuzz(8)).toEqual(8);
});

And the code to make it pass:

1
2
3
4
5
6
7
8
9
10
11
12
13
function fizzbuzz(n) {
  if (n % 3 === 0) {
    if (n % 5 === 0) {
      return fizzbuzz;
    } else {
      return fizz;
    }
  } else if (n % 5 === 0) {
    return buzz;
  } else {
    return n;
  }
}

Wrapping up

Now that we’ve covered the basics of TDD and of using Jasmine, I’d like to think you’re convinced of the benefits of testing your JavaScript, and have the drive to dig deeper into testing. I’d love to hear your opinions on this so for any comments and questions, let me know on Twitter.