TDD Basics : Make Your Code Robust

Objective


  • Learn to make the code robust

Discussion


In the previous article TDD Beyond Basics : Test Precisely and Concretely we worked on a simple stack implementation that deals with normal cases. To make that code robust, it's time to think about cases such as:

  • Extreme cases: 0, negative, null, maximums, etc
  • User error: What happens if the user passes in null or a negative value?

These cases do not seem to be relevant to our stack example but it's a good starting point. So, here is the list of relevant test cases:

  • A stack is empty on construction
  • After n pushes to an empty stack, n > 0, the stack is not empty and its size is n
  • If one pushes x then pops, the value popped is x.
  • If one pushes x then peeks, the value returned is x, but the size stays the same
  • If the size is n, then after n pops, the stack is empty and has a size 0
  • Popping from an empty stack throws an Exception
  • Peeking into an empty stack throws an Exception

Steps


Step 1

  it 'should be empty on construction' do
    stack = Stack.new

    expect(stack.empty?).to be(true)
  end

Let's define empty? method to get past 'undefined method empty?' error.

Step 2

  def empty?
    @elements.empty?
  end

The test passes. Split the second test case into two tests in order to test one thing at a time.

Step 3

  it 'after n pushes to an empty stack, n > 0, the stack is not empty' do

  end

  it 'after n pushes to an empty stack, n > 0, its size is n' do

  end

Step 4

  it 'after n pushes to an empty stack, n > 0, the stack is not empty' do
    stack = Stack.new
    stack.push(1)
    stack.push(2)

    expect(stack.empty?).to be(false)
  end

This test passes due to existing implementation.

Step 5

  it 'after n pushes to an empty stack, n > 0, its size is n' do
    stack = Stack.new
    stack.push(1)
    stack.push(2)

    expect(stack.size).to eq(2)
  end

passes.

Let's update the existing test as follows:

Step 6

  it 'should push a given item' do
    stack = Stack.new

    stack.push(1)

    expect(stack.pop).to eq(1)
  end 

Here we only have one assertion. We updated this test to eliminate overlap in the tests.

Step 7

  it 'if one pushes x then peeks, the value returned is x, but the size stays the same' do
    stack = Stack.new
    stack.push(1)

    expect(stack.peek).to eq(1)
    expect(stack.size).to eq(1)
  end

This fails.

  def peek
    @elements.last
  end

Since I had two assertions, I made the test fail by changing the assertion to eq(2) to make the test fail for the second assertion.

Step 8

  it 'If the size is n, then after n pops, the stack is empty and has a size 0' do
    stack = Stack.new
    stack.push(1)
    stack.push(2)

    stack.pop
    stack.pop

    expect(stack.empty?).to be(true)
    expect(stack.size).to eq(0)
  end

Exercises


Implement the last two test cases:

  • Popping from an empty stack throws an Exception
  • Peeking into an empty stack throws an Exception

Give clear message in the exception why the failure occurred and how to overcome the failure.

References


  1. Cracking the Coding Interview by Gayle Laakmann McDowell
  2. Stacks


Related Articles


Software Compatibility Best Practices

I spoke to some of the most talented and experienced software developers. I have created a guide that is filled with valuable insights and actionable ideas to boost developer productivity.

You will gain a better understanding of what's working well for other developers and how they address the software compatibility problems.

Get the Guide Now