tags:

views:

56

answers:

2

I have seen what it seems to me are two approaches to BDD. The difference hinges on the location of the "when":

In approach 1 the when is part of the specification:

AnEmptyStack.isNoLongerEmptyAfterPush

In pure "given when then" terminology this is:

"Given an empty stack, when it is pushed, then it is no longer empty."

So the "when" is part of the specification method:

isNoLongerEmptyAfterPush(){ stack.push(anObject); Assert.notEmpty(stack); }

In approach 2 the when is defined at the class level. That is, the when is usually invoked in the setup.

class WhenAnEmptyStackIsPushed(){

setup(){ stack.push(); }

public void thenItIsNotEmpty(){ assert(stack.notEmpty()) } }

Is there a preferred method? In terms of pure testing of behaviour, the second option seems preferable to me, since the focus of the test fixture is on the behaviour.

However, for ease of testing, I'm leaning towards the first method. Most of the pain I find in testing is the setup. That is, I have to get a SUT in a particular state. Once in that state usually its only one line of code to actually invoke some behaviour on it. So, having multiple behaviours per class (that is, per setup context) leverages the one time setup of the class.

So, I'm looking for thoughts. Is one approach preferred over the other?

A: 

I think your alternative 2 the preferable one. In my opinion each test class should setup the SUT in one state and each test method is then observations on that object. I think it makes more sense if you add a number of more observations to the class. If each observation is only and observation without additional actions I think you'll see how all observations naturally belong together.

If you go with alternative 1 you're not grouping observations because they observe different aspects of the same object (state) but rather because they have some common initial state you want to reuse. Don't group tests to reuse code, group tests because they belong together and use other means to reuse code as helper classes/methods or even inheritance (i.e. all Stack related classes could inherit from a class that creates an empty stack).

Cellfish
A: 

Depending on your testing framework, you can perhaps have the best of both worlds.

When I create a set of tests around a sut, I first declare a class that will wrap the whole set of specifications, then an abstract class:

public class SomethingDoerSpecs
{

    public abstract class concern : observations_for_a_sut_with_a_contract<IDoSomething,SomethingDoer>
    {
        // here I can define setup that will be common to all subsequent tests
        context c = () => ...
    }

    public class When_asked_to_do_something : concern
    {
        context c = () =>
        {
            // setup specific to this context goes here
        };

        because b = () => sut.DoSomething();

        it should_open_a_database_connection =
             () => mock_db_connection.was_told_to(x => x.Open());

        it should_set_the_result_value_to_true =
             () => sut.Result.should_be_true();

        // etc.
    }

   public class When_asked_to_do_something_but_the_database_is_unavailable
        : When_asked_to_do_something
    {
       context c = () =>
         {
            // additional context
         };

         because b = doing(() => sut.DoSomething());

         it should_throw_a_custom_exception = () =>
         {
            exception_thrown_by_the_sut.should_not_be_null();
             exception_thrown_by_the_sut
                 .should_be_an_instance_of<CouldNotDoSomethingException>();
         };

    }
}

This is just to illustrate that test classes can often be nested, so you can still do the "big" When... and reuse the state that you have set up previously by inheriting when you need greater context specificity. Of course, you have to be sure that your framework is going to reset the setup between sets of assertions.

By the way, the whole delegate syntax I'm showing here is from Jean-Paul Boodhoo's DevelopWithPassion.Bdd library, which you can find on Github.

Jay