Creating a Stack - Test-First

First Test

[Fact]
public void Create_Stack() //Nothing()
{
    Stack myStack = new Stack();
}
public class Stack
{

}

Stack is empty

[Fact]
public void Stack_Is_Empty() //Create_Stack() //Nothing()
{
    Stack myStack = new Stack();
    Assert.True(myStack.IsEmpty);
}
    public bool IsEmpty => true;

Push onto Stack

[Fact]
public void Stack_Is_Not_Empty()
{
    Stack stack = new Stack();
    stack.Push(0);
    Assert.False(stack.IsEmpty)
}
public void Push(int number)
{
    _isEmpty = false;
}
private bool _IsEmpty = true;
public bool IsEmpty => _IsEmpty;

Refactor

public class StackTest
{
    Stack stack = new Stack();
    [Fact]
    public void Stack_Is_Empty()
    {
        Assert.True(stack.IsEmpty);
    }
    [Fact]
    public void Stack_Is_Not_Empty()
    {
        stack.Push(0);
        Assert.False(stack.IsEmpty)
    }
}

Pop

[Fact]
public void Will_Throw_Underflow_When_Empty_Stack_Is_Popped()
{
    Assert.Throws<UnderflowException>(() => stack.Pop())
}
// 1)  Inner class
public class UnderflowException : Exception {}

// 2) 
public void Pop()
{
    throw new UnderflowException();
}

Push and Pop

[Fact]
public void After_One_Push_And_One_Pop_Stack_Will_Be_Empty()
{
    stack.Push(0);
    stack.Pop();
    Assert.True(stack.IsEmpty);
}
public void Pop()
{
    if (IsEmpty)
        throw new UnderflowException();
    _IsEmpty = true
}

Push Twice, Pop Once Test

Finally, a test to make me write a better solution.
Rule: Everything you do to the tests makes them more specific. Everything you do to the production code makes it more general.

[Fact]
public void After_Two_Pushes_And_One_Pop_Stack_Will_Not_Be_Empty()
{
    stack.Push(0);
    stack.Push(7);
    stack.Pop();
    Assert.False(stack.IsEmpty);
}

Push Twice, Pop Once Solution

public class Stack
{
    private int _Size = 0;
    public bool IsEmpty => _size == 0;
    public void Push(int number)
    {
        _Size++;
    }
    public void Pop()
    {
        if (IsEmpty)
            throw new UnderflowException();
        _Size--;
    }
    public class UnderflowException : Exception {}
}

After Pushing X will Pop X

Rule: Don't go for the gold! Avoid the central behaviour for as long as you can.
The "gold" in this case is LIFO.

[Fact]
public void After_Pushing_X_Will_Pop_X()
{
    stack.Push(99);
    int actual = stack.Pop();
    Assert.Equal(actual, 99);
}

Push X Pop X Solution

public int Pop()
{
    if (IsEmpty)
        throw new UnderflowException();
    _Size--;
    return 99;
}

Augment the test

Move from a [Fact] to a [Theory] by using different expectations.

[InlineData(99)]
[InlineData(77)]
[Theory]
public void After_Pushing_X_Will_Pop_X(int expected)
{
    stack.Push(expected);
    int actual = stack.Pop();
    Assert.IsEqual(actual, expected);
}

Make the test pass

private int _Element = -1;

public void Push(int number)
{
    _Element = number;
    _Size++;
}
public int Pop()
{
    if (IsEmpty)
        throw new UnderflowException();
    _Size--;
    return _Element;
}

Push Twice Pop Twice

Time to go for the gold

[Fact]
public void After_Pushing_X_Then_Y_Will_Pop_Y_Then_X()
{
    stack.Push(99);
    stack.Push(88);

    Assert.Equal(88, stack.Pop());
    Assert.Equal(pp, stack.Pop());
}

Make the test pass

private int[] _Element = new int[2];

public void Push(int number)
{
    _Element[_Size] = number;
    _Size++;
}

public int Pop()
{
    if (IsEmpty)
        throw new UnderflowException();
    int element = _Element[_Size - 1];
    _Size--;
    return element;
}

Final Refactorings

  • Rename the _Size to _LogicalSize, as it's more meaningful

Summary

The counter is the critical part of a LIFO behaviour.

  • Watch the Video of Uncle Bob
    • Listen especially to his comments after the code demo
    • Who taught Uncle Bob about Test Driven Development?
    • How long had Uncle Bob been programming before he learned TDD?
  • Then...

Try This Now

  • Build on this demo
    • Test if the stack is full
    • What happens if you push when the stack is full?
    • What if you want to set the stack's upperLimit when you create the stack?

The End