A Taste of TDD
Let’s return to the Box software and give it the ability to indicate whether it is empty or not.
This example is written in JavaScript using Jasmine. In case you want to follow along, you won’t need anything except a text editor, a browser, and a download of the Jasmine library.
Write a single test for a tiny bit of behavior:
describe("box", function() { it ("starts out empty", function() { var box = new Box(); expect(box.isEmpty()).toBeTruthy(); }); });Write just enough code so your test fails due to an assertion (also known as an expectation):
Box = function() { }; Box.prototype = { isEmpty: function() { return false; }, };Run all of the tests—that is, develop the habit of running the whole suite each time. Make sure the new test fails with an informative message, as illustrated in Figure 1-2.
Figure 1-2 Jasmine’s SpecRunner.html page showing the clean test failure.
(Source: Jasmine Software. Screenshot of testing, 2025.)
Note that Jasmine highlights the name of the test that has failed. The test (“spec”) name and failure message together describe the expected behavior. Seeing the test fail and reading the failure message is how you “test the test”: You know you’ve asked for behavior that has not yet been implemented.
Write just enough code to get the test to pass as quickly as possible:
isEmpty: function() { return true; },All we did was change false to true. This technique, called Fake It, will be covered in detail in Chapter 2, “Basic Moves.”
Run all of the tests again. They should all pass now, as shown in Figure 1-3.
Figure 1-3 Jasmine’s test-results browser page showing the tests passing.
(Source: Jasmine Software. Screenshot of testing, 2025.)
We’re not done with this behavior, but this seemingly trivial code is sufficient to pass all existing tests.
Refactor diligently!
Because this is our very first test, there’s nothing to refactor here. Nevertheless, you should always take a moment to review both the tests and the implementation to see if you can reduce any duplication or improve the clarity.
Back to step 1!
Write a single test …
Where there’s a true, there’s usually a false—for example, when a Box isn’t empty. We’ll give Box an add() method:
describe("box", function() { it ("isn’t empty after adding", function() { var box = new Box(); box.add("red pen"); expect(box.isEmpty()).toBeFalsy(); }); it ("starts out empty", function() { var box = new Box(); expect(box.isEmpty()).toBeTruthy(); }); });Write just enough code to make your test fail cleanly. It won’t fail cleanly until it has a stub for add(). Resist the temptation to write an implementation for add() before you see the test fail:
Box = function() { }; Box.prototype = { add: function(item) { }, isEmpty: function() { return true; }, };Run all of the tests.
Of course, the new test fails, as illustrated in Figure 1-4.
Write just enough code to get both tests to pass:
Box = function() { this.items = []; }; Box.prototype = { add: function(item) { this.items.push(item); }, isEmpty: function() { return this.items.length === 0; }, };Run all of the tests to see them pass, as shown in Figure 1-5.
Refactor diligently!
Refactoring the tests is as important as refactoring the implementation. Doing so often helps you write later tests.
There is a tiny bit of duplication between the two tests, and that’s enough. The duplicated code is highlighted in the following code:
describe("box", function() { it ("isn’t empty after adding", function() { var box = new Box(); box.add("red pen"); expect(box.isEmpty()).toBeFalsy(); }); it ("starts out empty", function() { var box = new Box(); expect(box.isEmpty()).toBeTruthy(); }); });All unit-testing frameworks have a way to perform common setup for each test in the suite. In Jasmine, it’s called beforeEach(), and it runs once before each test within the containing describe block:
describe("box", function() { var box; beforeEach(function() { box = new Box(); }); it ("isn’t empty after adding", function() { box.add("red pen"); expect(box.isEmpty()).toBeFalsy(); }); it ("starts out empty", function() { expect(box.isEmpty()).toBeTruthy(); }); });Another run of the tests confirms that the changes made did not break any tests (Figure 1-6).
Here’s what we’ve accomplished with this tiny example:
We have some tested, production-quality code.
We have written part of an engineering specification.
We have written two distinct behaviors that this object provides.
We’ve reduced duplication in the tests.
In Chapter 2, we’ll walk through a richer example and explore more test-driven thinking and design choices.



