Trying out Behave#

One of the neat things I saw at the Agile conference was a short demo of RSpec and RBehave. Intrigued, I did a quick search and found the .NET equivalents: Behave# and NSpec.

Note: I'm not sure I completely understand the differences between xSpec and xBehave as they both quote Behaviour-Driven Design as their goal. I think that the Behave libraries are just a way to change the nomenclature of xUnit from 'test' to 'behaviour'. The Spec ones are about writing user stories in a sort of DSL that a product owner or PM could understand.

Since developer stories can be user stories, I thought it would fun to use Behave# to implement the Stack example from Jim Newkirk's Test-Driven Development in .NET book. Here's what I ended up with (apologies for the formatting - it's much easier to read inside VS):

Stack stack = null;
bool isEmpty = false;
object tempObject = null, tempObject2 = null, tempObject3 = null;
List<int> knownObjects = new List<int>();
knownObjects.Add(1);
knownObjects.Add(2);
knownObjects.Add(3);

Story stackStory = new Story("Basic stack operations");

stackStory
.AsA("developer")
.IWant("to peform operations with a stack")
.SoThat("I can save my data");

stackStory
.WithScenario("A brand new stack")

.Given("a stack I created", delegate { stack = new Stack(); })
.When("I query its state", delegate { isEmpty=stack.IsEmpty; })
.Then("it should be empty", delegate { Assert.True(isEmpty); })

.Given("a stack I created")
.When("I push a single object",

          delegate { stack.Push(new object()); })
.And("I query its state")
.Then("it should not be empty",

          delegate { Assert.False(isEmpty); })

.Given("a stack I created")
.When("I push a single object")
.And("pop the object", delegate {tempObject=stack.Pop(); })
.And("I query its state")
.Then("it should be empty")

.Given("a stack I created")
.When("I push a known object",

          delegate { stack.Push(knownObjects[0]); })
.And("pop the object")
.Then("the two objects are equal",

          delegate {

               Assert.Equal<object>

                    (knownObjects[0], tempObject); })

.Given("a stack I created")
.When("I push three known objects",

          delegate{knownObjects.ForEach(

          delegate(int val){stack.Push(val);});})
.And("pop each one ",

          delegate { tempObject = stack.Pop();

                     tempObject2 = stack.Pop();

                     tempObject3 = stack.Pop(); })
.Then("all three objects are the same",
         delegate
         {
           Assert.Equal<object>(knownObjects[2], tempObject);
           Assert.Equal<object>(knownObjects[1], tempObject2);
           Assert.Equal<object>(knownObjects[0], tempObject3);
})

.Given("a stack I created")
.When("I pop an object it throws an exception",

         typeof(InvalidOperationException),

         delegate(Type type)

              { Assert.Throws(type, delegate { stack.Pop(); }); })
.Then("", delegate { ;})

.Given("a stack I created")
.When("I push a single object")
.And("call top", delegate { tempObject = stack.Top(); })
.And("I query its state")
.Then("it should not be empty")

.Given("a stack I created")
.When("I push a known object")
.And("call top")
.Then("the two objects are equal")

.Given("a stack I created")
.When("I push three known objects")
.And("call top")
.Then("the last item is equal to the top one",

         delegate

         { Assert.Equal<object>(knownObjects[2], tempObject); })

.Given("a stack I created")
.When("I push a known object")
.And("call top repeatedly",

         delegate

         {

            tempObject = stack.Top();

            tempObject2 = stack.Top();

            tempObject3 = stack.Top();

         })
.Then("all objects are equal to what was pushed",
         delegate
         {
           Assert.Equal<object>(knownObjects[0], tempObject);
           Assert.Equal<object>(knownObjects[0], tempObject2);
           Assert.Equal<object>(knownObjects[0], tempObject3);
})

.Given("a stack I created")
.When("I call top it throws an exception",

         typeof(InvalidOperationException),

         delegate(Type type)

           { Assert.Throws(type, delegate { stack.Top(); }); })
.Then("")

.Given("a stack I created")
.When("I push a null object", delegate { stack.Push(null); })
.And("I query its state")
.Then("it should not be empty")

.Given("a stack I created")
.When("I push a null object")
.And("pop the object")
.Then("the returned object is null",

          delegate { Assert.Null(tempObject); })

.Given("a stack I created")
.When("I push a null object")
.And("call top")
.Then("the returned object is null");

}

and when I run the tests, this is the output:

 

Story: Basic stack operations

Narrative:
    As a developer
    I want to peform operations with a stack
    So that I can save my data

    Scenario 1: A brand new stack
        Given a stack I created
        When I query its state
        Then it should be empty

        Given a stack I created
        When I push a single object
            And I query its state
        Then it should not be empty

        Given a stack I created
        When I push a single object
            And pop the object
            And I query its state
        Then it should be empty

        Given a stack I created
        When I push a known object
            And pop the object
        Then the two objects are equal

        Given a stack I created
        When I push three known objects
            And pop each one
        Then all three objects are the same

        Given a stack I created
        When I pop an object it throws an exception: System.InvalidOperationException
        Then

        Given a stack I created
        When I push a single object
            And call top
            And I query its state
        Then it should not be empty

        Given a stack I created
        When I push a known object
            And call top
        Then the two objects are equal

        Given a stack I created
        When I push three known objects
            And call top
        Then the last item is equal to the top one

        Given a stack I created
        When I push a known object
            And call top repeatedly
        Then all objects are equal to what was pushed

        Given a stack I created
        When I call top it throws an exception: System.InvalidOperationException
        Then

        Given a stack I created
        When I push a null object
            And I query its state
        Then it should not be empty

        Given a stack I created
        When I push a null object
            And pop the object
        Then the returned object is null

        Given a stack I created
        When I push a null object
            And call top
        Then the returned object is null

1 passed, 0 failed, 0 skipped, took 1.15 seconds.

So is it useful? Like most things, there are situations where it's appropriate and others where it's not. I'm starting with a contrived example to begin with, and so not demonstrating its full usefulness. Regardless, there are a few points I'd like to make after playing with it for a while:

  • It's nice to repeat snippets of an English sentence instead of code, although I suspect the novelty would fade after a while :)
  • If you want to specify Exceptions as part of your story, this forces you to move away from [ExpectedException]. Which is a Good Thing anyway. I'm using an Assert replacement from Codeplex to accomplish this.
  • I'm a big fan of having very descriptive method names for unit tests - this is taking descriptive to the next level :)

On the (possibly) less good side of things:

  • If a story fails, then the rest of the test isn't executed. It doesn't make sense to only put one scenario per test either, because then you'll end up repeating the delegate code.

  • If a story fails, it's sometimes really awkward to figure out why. It's really annoying when you have to debug into the delegate.

  • The test/stories are dependent on each other. This is a very disconcerting concept coming from an EDD background.

  • It's very difficult to test loops, or perhaps I just haven't found a good way. If you look at the place where Top() is called repeatedly (fifth last story) and we check to make sure it always returns the same value, I had to use named objects to accomplish this. The original test looked like this:

    [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);    }}

    and I would claim that's easier to read. There's some flexibility lost there.

I'd like to see some more real world examples of this being used; this could be a powerful tool in the right circumstances. And of course as the library is enhanced, some of the things I've talked about may become more feasable.