Why Use Test-Driven Development

Understand why test-driven development is useful.

Test-driven development uses

Test-driven development (TDD) is sometimes defined as writing tests first. Although that’s an important part of the methodology, it’s not the essence. The essence of TDD is rapid iteration. You’ll find that you learn more quickly from iterating than you would from trying to plan out a complex program from the ground up. You’ll discover bad assumptions and potential pitfalls before you invest too much work. And you’ll find the process more enjoyable. It’s a smooth, incremental progression rather than an alternation between bursts of inspiration and plateaus of “What do I do next?”

The project for this chapter will be a solver for the classic programming challenge Fizz Buzz. Here are the rules of Fizz Buzz:

Write a program that prints the numbers from 1 to 100. But for multiples of three, print “Fizz” instead of the number, and for the multiples of five, print “Buzz.” For numbers that are multiples of both three and five, print “FizzBuzz.”

In this section, you’ll apply the TDD process to implement a function that takes a number and returns the appropriate Fizz Buzz output. First, you’ll write a single test, knowing that it’ll fail. Second, you’ll write an implementation that satisfies the test. Once the test is passing, you’ll use Git to save your progress.

Starting from failure

  • Create an index.js with a placeholder implementation of fizzBuzz(), so that your tests will have a valid referent:

    // index.js
    module.exports = (num) => `${num}`;
    
  • Now add an index.test.js with a test for a single Fizz Buzz rule:

    // index.test.js
    const fizzBuzz = require('./index');
    
    describe('fizzBuzz()', () => {
      it('returns "FizzBuzz" for multiples of 3 and 5', () => {
        expect(fizzBuzz(15)).toBe('FizzBuzz');
        expect(fizzBuzz(30)).toBe('FizzBuzz');
      });
    });
    
  • Run the test:

    $ npm test
    ...
    FAIL  ./index.test.js
      fizzBuzz()
        ✕ returns "FizzBuzz" for multiples of 3 and 5 (7ms)
    
      ● fizzBuzz() › returns "FizzBuzz" for multiples of 3 and 5
    
        expect(received).toBe(expected) // Object.is equality
    
        Expected value to be:
          "FizzBuzz"
        Received:
          "15"
    
          3 | describe('fizzBuzz()', () => {
          4 |   it('returns "FizzBuzz" for multiples of 3 and 5', () => {
        > 5 |     expect(fizzBuzz(15)).toBe('FizzBuzz');
          6 |     expect(fizzBuzz(30)).toBe('FizzBuzz');
          7 |   });
          8 | });
    
          at Object.it (index.test.js:5:32)
    
    Test Suites: 1 failed, 1 total
    Tests:       1 failed, 1 total
    Snapshots:   0 total
    Time:        0.771s, estimated 1s
    Ran all test suites.
    

You may have cringed when you saw that glowing red FAIL. After all, having tests fail against production code is bad. But having tests fail during development can be a good thing. It means that you’ve anticipated some way your code could fail. Think of every failing test you see during development as a potential bug you’ve preemptively squashed.

Run the npm test below, to see your test cases failing, and explore why it failed using all the feedback from the npm test.

Get hands-on with 1400+ tech skills courses.