Test Driven Development

Welcome to Container Industries Ltd. As the new engineer here you've been assigned the job of developing the company's container functionality.

The following requirements have made there way over to your desk:

  1. Items can be added to a container
  2. Items are not added to a container if doing so would exceed the container's weight limit.

Seems nice and simple. Let's use this as an excuse to explore the world of Test Driven Development (TDD) together. There's a really nice rhythm I want to get across in the this post:

  1. Write the tests first
  2. Run the tests often
  3. Only start adding new functionality when the tests are passing
  4. When the tests are failing, your sole intention should be to get them to a passing state. Sometimes this means fudging the code, but that's OK; it's all about taking tiny steps and iterating frequently to meet the requirements

1. Items can be added to a container

Here's the initial test.

# container_spec.rb
describe Container do
  it "can add items" do
  end
end

After running it we get:

$ rspec container_spec.rb
/Users/andy/dev/Ruby/tdd/container_spec.rb:2:in `<top (required)>': uninitialized constant Container (NameError)
  from /Users/andy/.gem/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1435:in `load'
  from /Users/andy/.gem/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1435:in `block in load_spec_files'
  from /Users/andy/.gem/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1433:in `each'
  from /Users/andy/.gem/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1433:in `load_spec_files'
  from /Users/andy/.gem/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:100:in `setup'
  from /Users/andy/.gem/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:86:in `run'
  from /Users/andy/.gem/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:71:in `run'
  from /Users/andy/.gem/ruby/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:45:in `invoke'
  from /Users/andy/.gem/ruby/2.4.0/gems/rspec-core-3.5.4/exe/rspec:4:in `<top (required)>'
  from /Users/andy/.gem/ruby/2.4.0/bin/rspec:22:in `load'
  from /Users/andy/.gem/ruby/2.4.0/bin/rspec:22:in `<main>'

Looks like we need to create the Container definition. We are going to be iterating frequently, so let's do the simplest thing we can right now to get the tests to pass. We'll add the Container definition right at the top of the spec file.

class Container
end
describe Container do
  it "can add items" do
  end
end
$ rspec container_spec.rb
.

Finished in 0.00091 seconds (files took 0.66202 seconds to load)
1 example, 0 failures

Our first passing test! Yess

It isn't actually asserting anything though, so let's start adding real tests.

How about we work backwards, writing the test under the assumption that all the variables we need already exist?

class Container
end
describe Container do
  it "can add items" do
    container.add pasta
    container.add cake
    expect(container.items).to include pasta
    expect(container.items).to include cake
  end
end
$ rspec container_spec.rb
F

Failures:

  1) Container can add items
     Failure/Error: container.add pasta

     NameError:
       undefined local variable or method `container' for #<RSpec::ExampleGroups::Container:0x007fcafe150f60>
       Did you mean?  contain_exactly
     # ./container_spec.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.00065 seconds (files took 1.28 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./container_spec.rb:4 # Container can add items

Alright, looks like we need to define the container attribute in the test.

class Container
end
describe Container do
  let(:container) { Container.new }

  it "can add items" do
    container.add pasta
    container.add cake
    expect(container.items).to include pasta
    expect(container.items).to include cake
  end
end
$ rspec container_spec.rb
F

Failures:

  1) Container can add items
     Failure/Error: container.add pasta

     NameError:
       undefined local variable or method `pasta' for #<RSpec::ExampleGroups::Container:0x007fd3a1974d58>
     # ./container_spec.rb:7:in `block (2 levels) in <top (required)>'

Finished in 0.00071 seconds (files took 0.27835 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./container_spec.rb:6 # Container can add items

…and we need to define pasta and cake too.

# ...
describe Container do
  let(:container) { Container.new }
  let(:pasta) { "pasta" }
  let(:cake) { "cake" }

  it "can add items" do
    container.add pasta
    container.add cake
    expect(container.items).to include pasta
    expect(container.items).to include cake
  end
end
$ rspec container_spec.rb
F

Failures:

  1) Container can add items
     Failure/Error: container.add pasta

     NoMethodError:
       undefined method `add' for #<Container:0x007f969fa0ba58>
     # ./container_spec.rb:9:in `block (2 levels) in <top (required)>'

Finished in 0.00078 seconds (files took 0.27569 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./container_spec.rb:8 # Container can add items

The test is failing because we haven't implemented the add method. So let's add that next.

class Container
  def add(item)
  end
end
# ...
$ rspec container_spec.rb
F

Failures:

  1) Container can add items
     Failure/Error: expect(container.items).to include pasta

     NoMethodError:
       undefined method `items' for #<Container:0x007fce65a16b08>
     # ./container_spec.rb:13:in `block (2 levels) in <top (required)>'

Finished in 0.00068 seconds (files took 0.25235 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./container_spec.rb:10 # Container can add items

Now, it's time to tell Container that it has some items

class Container
  attr_reader :items
  def add(item)
  end
end
# ...
$ rspec container_spec.rb
F

Failures:

  1) Container can add items
     Failure/Error: expect(container.items).to include pasta
       expected nil to include "pasta", but it does not respond to `include?`
     # ./container_spec.rb:14:in `block (2 levels) in <top (required)>'

Finished in 0.25862 seconds (files took 0.32494 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./container_spec.rb:11 # Container can add items

This test is failing because our add method isn't implemented yet. So let's tackle that next.

class Container
  attr_reader :items

  def initialize
    @items = []
  end

  def add(item)
    items << item
  end
end
# ...
$ rspec container_spec.rb
.

Finished in 0.00198 seconds (files took 0.22488 seconds to load)
1 example, 0 failures

Here's what container_spec.rb looks like so far:

class Container
  attr_reader :items

  def initialize
    @items = []
  end

  def add(item)
    items << item
  end
end
describe Container do
  let(:container) { Container.new }
  let(:pasta) { "pasta" }
  let(:cake) { "cake" }

  it "can add items" do
    container.add pasta
    container.add cake
    expect(container.items).to include pasta
    expect(container.items).to include cake
  end
end

2. Items are not added to a container if doing so would exceed the container's weight limit

OK, here's my test:

  # ...
  it "does not add items that would exceed the weight limit" do
    container.add pasta
    container.add house
    expect(container.items).to include pasta
    expect(container.items).to_not include house
  end
  # ...
$ rspec container_spec.rb
.F

Failures:

  1) Container does not add items that would exceed the weight limit
     Failure/Error: container.add house

     NameError:
       undefined local variable or method `house' for #<RSpec::ExampleGroups::Container:0x007fe2a68d2cd8>
     # ./container_spec.rb:26:in `block (2 levels) in <top (required)>'

Finished in 0.00256 seconds (files took 0.24082 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./container_spec.rb:24 # Container does not add items that would exceed the weight limit

This fails because we haven't told the test what a house is.

What does it mean to be a house? Let's say it means a string with the value "house" for now.

describe Container do
  let(:container) { Container.new }
  let(:pasta) { "pasta" }
  let(:cake) { "cake" }
  let(:house) { "house" }

  # ...
$ rspec container_spec.rb
.F

Failures:

  1) Container does not add items that would exceed the weight limit
     Failure/Error: expect(container.items).to_not include house
       expected ["pasta", "house"] not to include "house"
     # ./container_spec.rb:29:in `block (2 levels) in <top (required)>'

Finished in 0.1118 seconds (files took 0.44737 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./container_spec.rb:25 # Container does not add items that would exceed the weight limit

Cool, a proper failure! The house was added to the container, when instead we should have cast it asunder.

What's the quickest way we can get the test to pass? Before we figure out the logic for deciding if we should add an item based on its weight, let's get the tests green by only adding the item if it isn't equal to "house".

class Container
  attr_reader :items

  def initialize
    @items = []
  end

  def add(item)
    items << item unless item == "house"
  end
end
# ...
$ rspec container_spec.rb
..

Finished in 0.00256 seconds (files took 0.21494 seconds to load)
2 examples, 0 failures

Whoa, the tests pass!

That's a bit weird, right? We fudged the code to make the tests pass. The key thing here is that we have a really tight red, green, refactor cycle; we write the tests, do the bear minimum to get them passing, then refactor to clean things up. We're doing the smallest thing we can to get to a "safe" (tests passing) state, before adding more code. That way we don't stray too far away from the safety of passing tests and don't get sidetracked implementing everything at once.

Now that the tests are passing we can refactor our add method to take into account an item's weight.

class Container
  attr_reader :items

  def initialize
    @items = []
  end

  def add(item)
    items << item unless item_too_heavy? item
  end

  private

  def item_too_heavy? item
    item == "house"
  end
end
# ...
$ rspec container_spec.rb
..

Finished in 0.00367 seconds (files took 0.24654 seconds to load)
2 examples, 0 failures

We've extracted the logic that checks if an item is too heavy into its own method. The tests pass, but we don't really have any way to define how heavy an item is. Let's amend the tests to specify the weights of items.

# ...
describe Container do
  let(:container) { Container.new }
  let(:pasta) { Item.new(5) }
  let(:cake) { Item.new(5) }
  let(:house) { Item.new(20) }

  # ...
end
$ rspec container_spec.rb
FF

Failures:

  1) Container can add items
     Failure/Error: let(:pasta) { Item.new(5) }

     NameError:
       uninitialized constant Item
     # ./container_spec.rb:20:in `block (2 levels) in <top (required)>'
     # ./container_spec.rb:25:in `block (2 levels) in <top (required)>'

  2) Container does not add items that would exceed the weight limit
     Failure/Error: let(:pasta) { Item.new(5) }

     NameError:
       uninitialized constant Item
     # ./container_spec.rb:20:in `block (2 levels) in <top (required)>'
     # ./container_spec.rb:32:in `block (2 levels) in <top (required)>'

Finished in 0.00088 seconds (files took 0.23031 seconds to load)
2 examples, 2 failures

Failed examples:

rspec ./container_spec.rb:24 # Container can add items
rspec ./container_spec.rb:31 # Container does not add items that would exceed the weight limit

The tests fail because we haven't defined Item yet. Let's do that now (still inside container_spec.rb for now):

# ...
class Item
  attr_reader :weight
  def initialize(weight)
    @weight = weight
  end
end
describe Container do
  let(:container) { Container.new }
  let(:pasta) { Item.new(5) }
  let(:cake) { Item.new(5) }
  let(:house) { Item.new(20) }

  # ...
end
$ rspec container_spec.rb
.F

Failures:

  1) Container does not add items that would exceed the weight limit
     Failure/Error: expect(container.items).to_not include house

       expected [#<Item:0x007fa7481ab5e8 @weight=5>, #<Item:0x007fa7481ab138 @weight=20>] not to include #<Item:0x007fa7481ab138 @weight=20>
       Diff:
       @@ -1,2 +1,2 @@
       -[#<Item:0x007fa7481ab138 @weight=20>]
       +[#<Item:0x007fa7481ab5e8 @weight=5>, #<Item:0x007fa7481ab138 @weight=20>]
     # ./container_spec.rb:41:in `block (2 levels) in <top (required)>'

Finished in 0.1253 seconds (files took 0.26481 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./container_spec.rb:37 # Container does not add items that would exceed the weight limit

Our implementation of the item_too_heavy? method only checks if an item is equal to "house". Since we're now using Items, and not strings, this test fails! Let's perform a similar check, but this time look for an item's weight being equal to 20 (the same as the house).

class Container
  attr_reader :items

  def initialize
    @items = []
  end

  def add(item)
    items << item unless item_too_heavy? item
  end

  private

  def item_too_heavy? item
    item.weight == 20
  end
end
# ...
$ rspec container_spec.rb
..

Finished in 0.00213 seconds (files took 0.22381 seconds to load)
2 examples, 0 failures

Now we have some passing tests we can refactor our item_too_heavy? method. The container needs to know about its weight limit to check if an item is too heavy.

class Container
  attr_reader :items

  def initialize
    @items = []
  end

  def add(item)
    items << item unless item_too_heavy? item
  end

  private

  def item_too_heavy? item
    weight_limit = 19
    item.weight > weight_limit
  end
end
# ...
$ rspec test.rb
..

Finished in 0.00234 seconds (files took 0.22688 seconds to load)
2 examples, 0 failures

Cool, we have our item_too_heavy? method taking in to account the container's weight limit. Since the tests are passing, we can look into cleaning this method up. One thing that jumps out here is that we aren't storing what the container's current weight is. That's important, so let's write a test for it.

# ...
describe Container do
  # ...

  it "updates the weight when an item is added" do
    container.add pasta
    expect(container.weight).to eq pasta.weight
    container.add cake
    expect(container.weight).to eq pasta.weight + cake.weight
  end
end

$ rspec container_spec.rb
..F

Failures:

  1) Container updates the weight when an item is added
     Failure/Error: expect(container.weight).to eq pasta.weight

     NoMethodError:
       undefined method `weight' for #<Container:0x007fae3f1764d0>
     # ./container_spec.rb:47:in `block (2 levels) in <top (required)>'

Finished in 0.0026 seconds (files took 0.23364 seconds to load)
3 examples, 1 failure

Failed examples:

rspec ./container_spec.rb:45 # Container updates the weight when an item is added

The Container class doesn't know about its current weight, so the test is failing. Let's add a weight attribute to the Container.

class Container
  attr_reader :items, :weight

  def initialize
    @items = []
    @weight = 0
  end

  def add(item)
    items << item unless item_too_heavy? item
  end

  private

  def item_too_heavy? item
    weight_limit = 19
    weight + item.weight > weight_limit
  end
end
# ...
$ rspec container_spec.rb
..F

Failures:

  1) Container updates the weight when an item is added
     Failure/Error: expect(container.weight).to eq pasta.weight

       expected: 5
            got: 0

       (compared using ==)
     # ./container_spec.rb:48:in `block (2 levels) in <top (required)>'

Finished in 0.16023 seconds (files took 0.26932 seconds to load)
3 examples, 1 failure

Failed examples:

rspec ./container_spec.rb:46 # Container updates the weight when an item is added

Although we're storing the weight on the container, we're never updating the value when we add items. This is why we expected a weight of 5 when it was actually 0.

class Container
  # ...

  def add(item)
    items << item unless item_too_heavy? item
    @weight += item.weight
  end

  # ...
end
# ...
$ rspec container_spec.rb
...

Finished in 0.00337 seconds (files took 0.26162 seconds to load)
3 examples, 0 failures

At this point our add method is looking OK, and I'm happy to move on. We can now look into refactoring other parts of the class. I think item_too_heavy? would be a good next step. It breaks the Single Responsibilty Principle in that it's responsible for setting the weight limit of the container and checking if an item is too heavy. By extracting this assignment into the constructor we are ensuring that item_too_heavy? only has one reason to change.

class Container
  attr_reader :items, :weight

  def initialize
    @items = []
    @weight = 0
    @weight_limit = 19
  end

  def add item
    items << item unless item_too_heavy? item
    @weight += item.weight
  end

  private

  def item_too_heavy? item
    weight + item.weight > @weight_limit
  end
end
# ...
$ rspec container_spec.rb
...

Finished in 0.00394 seconds (files took 0.21574 seconds to load)
3 examples, 0 failures

That's a bit nicer, but I'm still not really happy. We're hard coding the weight limit of our containers, which introduces a reason for the class to change in the future. Say Barry wants to update the weight limit to 15, he might be tempted to dive into the class and change the value. This introduces a risk that he'll accidentally break something while he's in there (you know what he's like). A more robust approach would be to inject the value of the container weight limit, that way Barry can have containers of varying weight limits without having to modify the Container class. Score!

Let's update the tests to inject the weight limit into the container when we instantiate it.

describe Container do
  let(:container) { Container.new(weight_limit: 19) }
  let(:pasta) { Item.new(5) }
  let(:cake) { Item.new(5) }
  let(:house) { Item.new(20) }

  # ...
end
$ rspec container_spec.rb
FFF

Failures:

  1) Container can add items
     Failure/Error:
       def initialize
         @items = []
         @weight = 0
         @weight_limit = 19
       end

     ArgumentError:
       wrong number of arguments (given 1, expected 0)
     # ./container_spec.rb:4:in `initialize'
     # ./container_spec.rb:28:in `new'
     # ./container_spec.rb:28:in `block (2 levels) in <top (required)>'
     # ./container_spec.rb:34:in `block (2 levels) in <top (required)>'

  2) Container does not add items that would exceed the weight limit
     Failure/Error:
       def initialize
         @items = []
         @weight = 0
         @weight_limit = 19
       end

     ArgumentError:
       wrong number of arguments (given 1, expected 0)
     # ./container_spec.rb:4:in `initialize'
     # ./container_spec.rb:28:in `new'
     # ./container_spec.rb:28:in `block (2 levels) in <top (required)>'
     # ./container_spec.rb:41:in `block (2 levels) in <top (required)>'

  3) Container updates the weight when an item is added
     Failure/Error:
       def initialize
         @items = []
         @weight = 0
         @weight_limit = 19
       end

     ArgumentError:
       wrong number of arguments (given 1, expected 0)
     # ./container_spec.rb:4:in `initialize'
     # ./container_spec.rb:28:in `new'
     # ./container_spec.rb:28:in `block (2 levels) in <top (required)>'
     # ./container_spec.rb:48:in `block (2 levels) in <top (required)>'

Finished in 0.00117 seconds (files took 0.21716 seconds to load)
3 examples, 3 failures

Failed examples:

rspec ./container_spec.rb:33 # Container can add items
rspec ./container_spec.rb:40 # Container does not add items that would exceed the weight limit
rspec ./container_spec.rb:47 # Container updates the weight when an item is added

Cool, we broke everything. Let's update the initialize method in Container to accept the weight limit.

class Container
  attr_reader :items, :weight

  def initialize(weight_limit:)
    @items = []
    @weight = 0
    @weight_limit = weight_limit
  end

  # ...
end
# ...
$ rspec container_spec.rb
...

Finished in 0.01101 seconds (files took 0.24302 seconds to load)
3 examples, 0 failures

Now that the tests are passing, we can perform more clean up. For example, we used a named argument for weight_limit in Container. Let's do the same thing for Item and its weight.

First, the tests…

# ...
describe Container do
  let(:container) { Container.new(weight_limit: 19) }
  let(:pasta) { Item.new(weight: 5) }
  let(:cake) { Item.new(weight: 5) }
  let(:house) { Item.new(weight: 20) }

  # ...
end
$ rspec container_spec.rb
FFF

Failures:

  1) Container can add items
     Failure/Error: weight + item.weight > @weight_limit

     TypeError:
       Hash can't be coerced into Integer
     # ./container_spec.rb:18:in `+'
     # ./container_spec.rb:18:in `item_too_heavy?'
     # ./container_spec.rb:11:in `add'
     # ./container_spec.rb:34:in `block (2 levels) in <top (required)>'

  2) Container does not add items that would exceed the weight limit
     Failure/Error: weight + item.weight > @weight_limit

     TypeError:
       Hash can't be coerced into Integer
     # ./container_spec.rb:18:in `+'
     # ./container_spec.rb:18:in `item_too_heavy?'
     # ./container_spec.rb:11:in `add'
     # ./container_spec.rb:41:in `block (2 levels) in <top (required)>'

  3) Container updates the weight when an item is added
     Failure/Error: weight + item.weight > @weight_limit

     TypeError:
       Hash can't be coerced into Integer
     # ./container_spec.rb:18:in `+'
     # ./container_spec.rb:18:in `item_too_heavy?'
     # ./container_spec.rb:11:in `add'
     # ./container_spec.rb:48:in `block (2 levels) in <top (required)>'

Finished in 0.00106 seconds (files took 0.29553 seconds to load)
3 examples, 3 failures

Failed examples:

rspec ./container_spec.rb:33 # Container can add items
rspec ./container_spec.rb:40 # Container does not add items that would exceed the weight limit
rspec ./container_spec.rb:47 # Container updates the weight when an item is added

Now we can fix up the Item class by updating the initialize method, just like we did for the Container class.

# ...
class Item
  attr_reader :weight
  def initialize(weight:)
    @weight = weight
  end
end
# ...
$ rspec container_spec.rb
...

Finished in 0.01256 seconds (files took 0.25995 seconds to load)
3 examples, 0 failures

Time to refactor!

This is what our Container's item_too_heavy? method currently looks like:

def item_too_heavy? item
  weight + item.weight > @weight_limit
end

Notice that we aren't using an accessor method to get the weight limit; we are calling @weight_limit to access it directly. Although this is fine here, it's generally best to use an accessor because it changes the statement from calling data (@weight_limit) to calling behaviour (weight_limit, the attr_reader method). This is useful because it means there is one place in our code that defines what it means to be a weight_limit. This perhaps sounds pedantic, but what if (for safety reasons) there was an update to weight limits, which meant that they were reduced by 5 when the inspectors were around. We would need to find everywhere we were calling the data @weight_limit and subtract 5 if the inspectors were near. We'd be duplicating behaviour and it'd be likely that something would break. By using an accessor method we could simply override the weight_limit method to include the suitable logic, and our other methods that relied on the accessor would not need to change.

Let's make use of Ruby's accessor methods to get the weight limit.

class Container
  attr_reader :items, :weight, :weight_limit

  def initialize(weight_limit:)
    @items = []
    @weight = 0
    @weight_limit = weight_limit
  end

  def add item
    items << item unless item_too_heavy? item
    @weight += item.weight
  end

  private

  def item_too_heavy? item
    weight + item.weight > weight_limit
  end
end
# ...
$ rspec container_spec.rb
...

Finished in 0.00201 seconds (files took 0.13739 seconds to load)
3 examples, 0 failures

While we're on removing unnecessary direct calls to instance variables, how about we clean up the add method by replacing

@weight += item.weight

with a call to an accessor method too?

class Container
  attr_reader :items, :weight, :weight_limit

  def initialize(weight_limit:)
    @items = []
    @weight = 0
    @weight_limit = weight_limit
  end

  def add item
    items << item unless item_too_heavy? item
    self.weight += item.weight
  end

  private

  def item_too_heavy? item
    weight + item.weight > weight_limit
  end

  def weight=(value)
    @weight = value
  end
end
# ...
$ rspec container_spec.rb
...

Finished in 0.00586 seconds (files took 0.28069 seconds to load)
3 examples, 0 failures

At this point, our container_spec.rb file is getting pretty hefty, so let's extract out the classes into their own files.

# container.rb
class Container
  attr_reader :items, :weight, :weight_limit

  def initialize(weight_limit:)
    @items = []
    @weight = 0
    @weight_limit = weight_limit
  end

  def add item
    items << item unless item_too_heavy? item
    self.weight += item.weight
  end

  private

  def item_too_heavy? item
    weight + item.weight > weight_limit
  end

  def weight=(value)
    @weight = value
  end
end
# item.rb
class Item
  attr_reader :weight
  def initialize(weight:)
    @weight = weight
  end
end
# container_spec.rb
require "./container"
require "./item"

describe Container do
  let(:container) { Container.new(weight_limit: 19) }
  let(:pasta) { Item.new(weight: 5) }
  let(:cake) { Item.new(weight: 5) }
  let(:house) { Item.new(weight: 20) }

  it "can add items" do
    container.add pasta
    container.add cake
    expect(container.items).to include pasta
    expect(container.items).to include cake
  end

  it "does not add items that would exceed the weight limit" do
    container.add pasta
    container.add house
    expect(container.items).to include pasta
    expect(container.items).to_not include house
  end

  it "updates the weight when an item is added" do
    container.add pasta
    expect(container.weight).to eq pasta.weight
    container.add cake
    expect(container.weight).to eq (pasta.weight + cake.weight)
  end
end
$ rspec container_spec.rb
...

Finished in 0.00203 seconds (files took 0.11261 seconds to load)
3 examples, 0 failures

Amazing, the tests still pass! Container Industries Ltd are proud of your hard work and you've been rewarded with an almost brand new tupperware container.

I really enjoy the TDD approach. In the past I've found myself trying to implement all the features at once and getting in too deep quite early on. In the end the code coverage was usually not as high and the code quality was lower than when I used TDD.

Do you use TDD regularly? What do you think of it? Do you use an alternative approach?

Previous post: Strategising your way to clean code

Next post: Typed Arrays in ECMAScript