In Chapter 1, Test-Driven Development Practices, we stated that the first step is to brainstorm a list of tests for the task. The goal of this activity is to create a test list that verifies the detailed requirements and describes the completion criteria. One thing to keep in mind is that the list is not static. As you implement each test, you might have to
Let s try to write the test list for the unbounded Stack .
Unbounded Stack Test List
Create a Stack and verify that IsEmpty is true.
Push a single object on the Stack and verify that IsEmpty is false.
Push a single object, Pop the object, and verify that IsEmpty is true.
Push a single object, remembering what it is; Pop the object, and verify that the two objects are equal.
Push three objects, remembering what they are; Pop each one, and verify that they are removed in the correct order.
Pop a Stack that has no elements.
Push a single object and then call Top . Verify that IsEmpty is false.
Push
a single object, remembering what it is; and then call
Top
. Verify that the object that is returned is the same as the one that was
Call Top on a Stack with no elements.
There are differing
A test in the list that is closer to the essence of the problem is the following:
Push
a single object, remembering what it is;
Pop
the object, and verify that it is equal to the object that was pushed. In this test, you are verifying that the
Push
and
Pop
There are times when the essence approach can take too much time to implement. If that is the case, you should choose a simpler test to get started. The suggestion that we give people who are learning TDD is to choose the simplest test approach and graduate to the essence approach after becoming familiar with the technique. Therefore, the first test that we chose to implement is Create an empty Stack and verify that IsEmpty is true.
[1] Beck, Kent. Test-Driven Development: By Example . Addison-Wesley, 2003.
The following is the implementation of each test in the test list.
This test requires creating a Stack and then calling the IsEmpty property. The IsEmpty property should return true because we haven t put any elements into the Stack . Let s create a file called StackFixture.cs, in which we write a test fixture class, called StackFixture , to hold the tests.
using System;
using NUnit.Framework;
[TestFixture]
public class StackFixture
{ /* */ }
There are a few things of interest about this class. The line
using NUnit.Framework;
is needed to reference the custom attributes defined in NUnit that are used to mark the test fixture. The
[TestFixture]
attribute can be associated only with a class. This attribute is an indicator to NUnit that the class contains test
The
[Test]
public void Empty()
{
Stack stack = new Stack();
Assert.IsTrue(stack.IsEmpty);
}
The test method is
Although the class used in the test, Stack , and the property IsEmpty do not exist, we are writing test code as if they do. We are thinking about how the class and its methods are used instead of how to implement it, which is an important distinction. This is why many people refer to test-driven development as much a design technique as a testing technique. Many times, class library designers implement a library and then figure out how to use it, which can lead to libraries that require a lot of initialization, complex method interactions, and increased dependencies. Thinking about how to use the library before implementing it places a greater emphasis on usage, which often leads to better design.
Because the Stack class does not exist, the test does not compile. That s easy enough to fix. What is the smallest amount of work that needs to be done to get this to compile?
using System;
public class Stack
{
private bool isEmpty = true;
public bool IsEmpty
{
get
{
return isEmpty;
}
}
}
This implementation is
public bool IsEmpty
{
get
{
return true;
}
}
There is a balance to achieve between
This discussion is also relevant to the earlier discussion about the test list. It is very clear from the test list that you have to store multiple items. Should you go ahead and use an ArrayList because you might need it later? No ”the current tests do not support the need for an ArrayList . Wait and see what the tests look like before making that decision.
Now that the code compiles, it is time to run the test in NUnit. The green bar displays, which indicates success. We can check off the first test and move on.
Which test should we choose next? Perhaps we should stay focused on the IsEmpty property because it is probably the smallest increment over what we have now. Let s look at Push a single object on the Stack and verify that IsEmpty is false.
Test 2 says to Push an object onto the Stack and then verify that IsEmpty returns false. Let s try and write a test that does this. We ll call the method PushOne :
[Test]
public void PushOne()
{
Stack stack = new Stack();
stack.Push("first element");
Assert.IsFalse(stack.IsEmpty,
"After Push, IsEmpty should be false");
}
The test, like the previous one, creates a Stack object. Then, using a method named Push puts a String object onto the Stack . Finally, we call the IsEmpty property on Stack and verify that it returns false.
Of course, this code does not compile because we have not defined the Push method. Once again, what is the minimal amount of work needed to get this code to compile?
public void Push(object element)
{
}
That is as small as it gets. Now, we can run the test. Running the tests yields the following result:
Tests run: 2, Failures: 1, Not run: 0, Time: 0.015627 seconds Failures: 1) StackFixture.PushOne : After Push, IsEmpty should be false at StackFixture.PushOne() in c:\stackfixture.cs:line 19
The test failed because it was expecting the
IsEmpty
property of the
Stack
to return false and it returned true. Now that we have a failing test, we can implement
Push
correctly. Clearly, we need to change the
isEmpty
member variable to be false when an element is
public void Push(object element)
{
isEmpty = false;
}
Before we decide which test to implement next, we need to make sure that there is no code duplication. The
StackFixture
class has some
using System;
using NUnit.Framework;
[TestFixture]
public class StackFixture
{
private Stack stack;
[SetUp]
public void Init()
{
stack = new Stack();
}
[Test]
public void Empty()
{
Assert.IsTrue(stack.IsEmpty);
}
[Test]
public void PushOne()
{
stack.Push("first element");
Assert.IsFalse(stack.IsEmpty,
"After Push, IsEmpty should be false");
}
}
We had to create a private instance variable called stack so that all the methods of the class could access the same object. The function Init is marked with an attribute called [ SetUp ]. NUnit uses this attribute to ensure that this method is called prior to each test being run, which means that each test method gets a newly created Stack , instead of one modified from a previous test.
This is an
Because the code compiled and passed the tests, it s time to move on to the next test. We want to stay focused on the IsEmpty property, so Push a single object, Pop the object and verify that IsEmpty is true seems like a natural.
This test introduces a new method called
Pop
, which returns the topmost element and
[Test]
public void Pop()
{
stack.Push("first element");
stack.Pop();
Assert.IsTrue(stack.IsEmpty,
"After Push - Pop, IsEmpty should be true");
}
Of course, the code does not compile because we haven t defined the method Pop . So we ll fake it: [2]
public void Pop()
{
}
The code compiles, but the tests fail with the following message:
Tests run: 3, Failures: 1, Not run: 0, Time: 0.0156336 seconds Failures: 1) StackFixture.Pop : After Push - Pop, IsEmpty should be true at StackFixture.Pop() in c:\stackfixture.cs:line 33
We now need to implement the
Pop
method so that it
public void Pop() { isEmpty = true; }
The code compiles and we run the tests. The tests pass, so we can check off Test 3 on the test list.
Notice that the implemented Pop method returns void . The requirements stated previously said that Pop should also return the topmost element. Because we do not have a test that tests that functionality, we will leave it that way until we have a failing test.
So far, we have been
In this test, we need to create an object (in this case, an int ), push the object onto the Stack , pop the Stack , and verify that the object that is returned is equal to the object pushed on the Stack . The following is the test method PushPopContentCheck :
[Test]
public void PushPopContentCheck()
{
int expected = 1234;
stack.Push(expected);
int actual = (int)stack.Pop();
Assert.AreEqual(expected, actual);
}
Of course, this code does not compile. The Pop method returns void , not object . So let s change the Pop method to return an object . The simplest code is to have it return null :
public object Pop() { isEmpty = true; return null; }
Let s compile and run the tests. The code compiles, but the tests fail with the following message:
Tests run: 4, Failures: 1, Not run: 0, Time: 0.0156311 seconds
Failures: StackFixture.PushPopContentCheck : System.NullReferenceException : Object reference not set to an instance of an object. at StackFixture.PushPopContentCheck() in c:\stackfixture.cs:line 42
The test failed because we did not return the value that was on the top of the Stack . In order for this test to pass, we have to change the Push method to retain the object and alter the Pop method to return the object. The following code is our next attempt:
using System;
public class Stack
{
private bool isEmpty = true;
private object element;
public bool IsEmpty
{
get
{
return isEmpty;
}
}
public void Push(object element)
{
this.element = element;
isEmpty = false;
}
public object Pop()
{
isEmpty = true;
object top = element;
element = null;
return top;
}
}
Let s compile and run the tests. All the tests pass, so we can now mark this test off the list. Before we move on, let s do a little refactoring because there is a change that could be made
using System;
public class Stack
{
private object element;
public bool IsEmpty
{
get
{
return (element == null);
}
}
public void Push(object element)
{
this.element = element;
}
public object Pop()
{
object top = element;
element = null;
return top;
}
}
This is much better because we use the
element
variable itself to represent whether the
Stack
is empty or not. Prior to this, we had to update two
We want the next test to verify that the Stack works as expected.
The previous test, PushPopContentCheck , pushed and popped only one item. In this test, we have to push three items to ensure that the Stack behaves in the correct fashion:
[Test]
public void PushPopMultipleElements()
{
string pushed1 = "1";
stack.Push(pushed1);
string pushed2 = "2";
stack.Push(pushed2);
string pushed3 = "3";
stack.Push(pushed3);
string popped = (string)stack.Pop();
Assert.AreEqual(pushed3, popped);
popped = (string)stack.Pop();
Assert.AreEqual(pushed2, popped);
popped = (string)stack.Pop();
Assert.AreEqual(pushed1, popped);
}
In the PushPopMultipleElements method, we push 3 items onto the Stack and then pop them off and verify that they are in the correct order. In this example, we push 1 , 2 , and 3 in that order and verify that when we call Pop repeatedly, the strings come off 3 , 2 , and 1 .
Let s compile and run the tests. The code compiles, but NUnit fails with the following message:
Tests run: 5, Failures: 1, Not run: 0, Time: 0.031238 seconds
Failures:
1) StackFixture.PushPopMultipleElements :
expected:<"2">
but was:<(null)>
at StackFixture.PushPopMultipleElements() in c:\stackfixture.cs:line 59
Clearly, something is wrong. In fact, we can no longer use the simplistic implementation of the Stack with a single element. We need to use a collection to hold the elements inside the Stack . After making a series of changes, here is the refactored Stack code:
using System;
using System.Collections;
public class Stack
{
private ArrayList elements = new ArrayList();
public bool IsEmpty
{
get
{
return (elements.Count == 0);
}
}
public void Push(object element)
{
elements.Insert(0, element);
}
public object Pop()
{
object top = elements[0];
elements.RemoveAt(0);
return top;
}
}
Let s compile and run the tests. The code compiles and all the tests pass, so we can move on. You should notice that we made big changes to the entire implementation, and no tests were broken afterward. This is a good example of building confidence in the code that we have tests for. We are assured that, based on our tests, the code is no
This example shows very well the benefits of delaying implementation decisions while writing tests. Some would argue that we should have started out using an
ArrayList
to hold the elements of the
Stack
because it was a foregone conclusion that we would need a collection to hold the elements. We did not do this because we are trying to let the tests drive the need for functionality instead of us thinking we know what is needed and then writing tests that verify that thinking. It is a difference that this test
So far, we have been careful to call Pop on a Stack only when it contains elements. In the next test, we need to look at what happens when we call Pop on a Stack that has no elements.
What should happen if we call Pop on the Stack and there are no elements? There are a number of options:
We could return null for the value (although we could never store null on the Stack ).
We could use an in/out parameter to
We could throw an exception because it s an error we don t expect to occur.
Reviewing the options, it seems to make the most sense to have the Pop method throw an exception if there are no elements on the Stack . Let s write a test that expects the Pop operation to throw an exception:
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void PopEmptyStack()
{
stack.Pop();
}
This test uses another attribute in NUnit that allows the programmer to declare that the execution of the test is expected to throw an exception. We could define a new exception, but we choose to use InvalidOperationException in this example. It is in the System namespace and is defined as The exception that is thrown when a method call is invalid for the object s current state. [3]
We compiled and ran the tests. The test failed with the following message:
Tests run: 6, Failures: 1, Not run: 0, Time: 0.0156248 seconds Failures: StackFixture.PopEmptyStack : Expected: InvalidOperationException but was ArgumentOutOfRangeException at System.Collections.ArrayList.get_Item(Int32 index) at Stack.Pop() in c:\projects\book\stack\stack.cs:line 23 at StackFixture.PopEmptyStack() in c:\stackfixture.cs:line 68
Not surprisingly, it does not work; we never changed the
Pop
method to return the exception. However, we did get an exception, just not the right one. The
ArgumentOutOfRangeException
occurs when you access an array outside of the
public object Pop()
{
if(IsEmpty) throw new
InvalidOperationException("cannot pop an empty stack");
object top = elements[0];
elements.RemoveAt(0);
return top;
}
That works. We can now check this test off the list and figure out which test to do next.
As we were implementing this test, a few additional tests came to mind, so we need to add them to our
test list
. We want to add tests to verify that the
Stack
works when the arguments are equal to
null
. The new tests are as
Push null onto the Stack and verify that IsEmpty returns false.
Push null onto the Stack , Pop the Stack , and verify that the value returned is null .
Push null onto the Stack , call Top , and verify that the value returned is null .
Reviewing the test list indicates that we have not done anything yet with the Top method, so let s focus on that next.
The Top method does not change the state of the Stack; it simply returns the topmost element. This test verifies that IsEmpty is not affected by the call to Top . Let s write that test:
[Test]
public void PushTop()
{
stack.Push("42");
stack.Top();
Assert.IsFalse(stack.IsEmpty);
}
Of course, this code does not compile. We have not written the Top method, so we ll fake it:
public object Top()
{
return null;
}
That works. So we ll check off another item on the list and decide which test to implement next.
Not so fast! There are a couple of tests that we need to add to the test list that we thought of while doing this test.
Push multiple items onto the Stack and verify that calling Top returns the correct object.
Push an item on the Stack , call Top repeatedly, and verify that the object returned each time is equal to the object that was pushed onto the Stack .
The test list now contains 14 items. These last two tests are important because they verify that Top works as expected. We didn t think of them at the beginning, but they came to mind when we started working on Top .
The next test we will implement verifies that Top returns the correct object.
In the previous test, we checked to see whether the IsEmpty property was correct after we called Top . In this test, we verify that the object pushed onto the Stack is equal to the one we get back when we call Top :
[Test]
public void PushTopContentCheckOneElement()
{
string pushed = "42";
stack.Push(pushed);
string topped = (string)stack.Top();
Assert.Equals(pushed, topped);
}
Let s compile and run the tests. NUnit protests with the following message:
Tests run: 8, Failures: 1, Not run: 0, Time: 0.0312442 seconds
Failures:
1) StackFixture.PushTopContentCheckOneElement :
expected:<"42">
but was:<(null)>
at StackFixture.PushTopContentCheckOneElement()
in c:\stackfixture.cs:line 84
In the previous test, we faked the implementation of Top by just returning null . Looks like we have to implement (make) it correctly for this test to pass:
public object Top()
{
return elements[0];
}
That works. Although Top seems similar to Pop , let s wait and see whether it gets more obvious as we add additional tests. Let s write another test.
This test states that we need to push more than one object onto the Stack and then call Top . The return value of Top should be equal to the last value that was pushed onto the Stack . Let s give it a try:
[Test]
public void PushTopContentCheckMultiples()
{
string pushed3 = "3";
stack.Push(pushed3);
string pushed4 = "4";
stack.Push(pushed4);
string pushed5 = "5";
stack.Push(pushed5);
string topped = (string)stack.Top();
Assert.AreEqual(pushed5, topped);
}
That works. This one just happens to work, so good for us. The next test verifies that calling Top repeatedly always returns the same object.
As stated previously, the Top method is not supposed to change the state of the object, so we should be able to push an object onto the Stack and then call Top as many times as we want ”and it should always return the same object. Let s code it and see whether it works:
[Test]
public void PushTopNoStackStateChange()
{
string pushed = "44";
stack.Push(pushed);
for(int index = 0; index < 10; index++)
{
string topped = (string)stack.Top();
Assert.AreEqual(pushed, topped);
}
}
That works, too.
Let s move on. The next test determines what happens when we call Top on a Stack that has no elements.
Consistency is a key component of designing a class library. Because we chose to throw an InvalidOperationException when we called Pop , we should be consistent and throw the same exception when we call Top . Let s write the test:
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void TopEmptyStack()
{
stack.Top();
}
Of course, this does not work. NUnit provides the details:
Tests run: 11, Failures: 1, Not run: 0, Time: 0.031263 seconds Failures: StackFixture.TopEmptyStack : Expected: InvalidOperationException but was ArgumentOutOfRangeException at System.Collections.ArrayList.get_Item(Int32 index) at Stack.Top() in c:\projects\book\stack\stack.cs:line 33 at StackFixture.TopEmptyStack() in c:\stackfixture.cs:line 119
You would think we would learn something ”this is the same failure we got when we first implemented Pop . We need something similar in Top :
public object Top()
{
if(IsEmpty) throw new
InvalidOperationException("cannot top an empty stack");
return elements[0];
}
This works. However, the similarity between Top and Pop is very apparent and needs to be refactored. They both check to see whether there are elements in the list and throw an exception if there aren t any. The best solution seems to have Pop call Top . Let s give that a try:
public object Pop()
{
object top = Top();
elements.RemoveAt(0);
return top;
}
public object Top()
{
if(IsEmpty)
throw new InvalidOperationException("Stack is Empty");
return elements[0];
}
This works: the duplication has been removed. We did have to make a change to the message that was in the exception ”it is more generic now, which is a small price to pay for consistency in the code. We can now check this test off the list. (There are only three more to go, so the end is in sight.)
This is the first test that was added when we wrote the test that called
Pop
on an empty
Stack
. As you might recall, one of the options was to return a
null
to indicate that there were no elements in the
Stack
. If we had
This test pushes null onto the Stack and verifies that IsEmpty is false:
[Test]
public void PushNull()
{
stack.Push(null);
Assert.IsFalse(stack.IsEmpty);
}
This works just fine. Let s do the next test.
The previous test
[Test]
public void PushNullCheckPop()
{
stack.Push(null);
Assert.IsNull(stack.Pop());
Assert.IsTrue(stack.IsEmpty);
}
Let s compile and run the test. It works! This test has two asserts: one to check whether the return value is null , and one to check that IsEmpty is true. They could be done separately, but we chose to combine them because they are associated with the same test setup.
Now there is only one test left. Does the Top method work when we push null onto the Stack ?
In this test, we push a null object onto the Stack and then call Top to retrieve the element from the Stack . The value returned from Top should be equal to null .
[Test]
public void PushNullCheckTop()
{
stack.Push(null);
Assert.IsNull(stack.Top());
Assert.IsFalse(stack.IsEmpty);
}
Compiling and running the tests indicate success. This is the last test on the list, and we can t think of any others. Although there are probably things we missed, we can t think of any for now ”so the task is complete. We can check the code into the repository and work on the next task. As
[2] Beck, Kent. Test-Driven Development: By Example . Addison-Wesley, 2003.
[3] NET SDK Documentation, System.InvalidOperationException