Question

I'm currently learning/testing BDD using SpecFlow, and it works great!

Before I choose to ask my question, I have read this one, and I felt like I had to ask my question despite the fact that the same problem is addressed, because of the Exception scenario which is not mentioned.

I'm actually testing this scenario:

Scenario: I should get an error whenever I try to remove an item from an empty stack
    Given I have an empty stack
    When  I pop from it
    Then  I should get an error

public class StackBehaviour {
    public void GivenIHaveAnEmptyStack() { stack = new CustomStack<string>(); }

    // This will throw whenever called!
    // So the Then method will never be run!
    // I feel like I should just put a comment which says why it's empty,
    // allowing a fellow programmer to understand the exact intention.
    public void WhenIPopFromIt() { stack.Pop(); }

    // It is here that it verifies whether the CustomStack meets the expected behaviour.
    public void ThenIShouldGetAnError() {
        Assert.Throws<IndexOutOfRangeException>(delegate {
            stack.Pop(); 
        });
    }

    private CustomStack<string> stack;
}

public class CustomStack<T> {
    public T Pop() { 
        if (stack.Count == 0) 
            throw new IndexOutOfRangeException("Cannot pop from an empty stack!");
        T item = stack[stack.Count-1];
        stack.RemoveAt(stack.Count-1);
        return item;
    }

    private ArrayList stack = new ArrayList();
}

I think that leaving a comment in the When method is correct, so that the business requirement doesn't lack any information, and on the code behind, I put it clear what my intention is exactly by commenting.

What do you think? Any other ideas why I shouldn't make it?

Was it helpful?

Solution

There is another trick that you can use where the bindings help make the feature's meaning a little clearer. In this case, let's start at the end.

Then I should get an error

Your problem is a basically here, you know want an error, but you don't know how to get it. In fact you've already missed it, the error has already occurred in the When step, and in your code example, you moved the action into the then step just so you could get the error.

But what if keep the when performing the action amd re-express our then to reflect what really happens

Then I should have had an error

Seems a trivial change but now our feature reflects that the error should have been associated with the When step and we are simply evaluating it later, and that is something we can code. We just need something to remember the error in the when and deliver it to the then.

private Exception errorFromWhen = null;

public void WhenIPopFromIt() 
{ 
  try
  {
    stack.Pop(); 
  }
  catch(Exception ex)
  {
     errorFromWhen = ex;
  }
}

public void ThenIShouldGetAnError() 
{
   errorFromWhen.ShouldNotBeNull();
   errorFromWhen.ShouldBe<IndexOutOfRangeException>();
}

SpecFlow has absolutely no problems with this, in fact due to its mini Dependency injection system, you can even pass this state between binding classes if necessary.

OTHER TIPS

May a scenario not have a When in BDD?

In Specflow, neither given, when or then are mandatory.

However in your example, I don't believe this is a good use of Specflow and BDD. In this answer here Marcus states:

"BDD is about ensuring that we're building the right thing, TDD is about ensuring that we're building it right."

In your example the scope of what is being tested i.e. the CustomStack, should be tested via TDD. It is the end solution that makes use of the CustomStack should be tested via BDD (and hence SpecFlow) e.g. if this CustomStack was being exercised via a certain action on a website.

This answer is also pertinent to your question.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top