Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BDD and the location of "when"

Tags:

bdd

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?

like image 613
pondermatic Avatar asked Nov 05 '22 18:11

pondermatic


1 Answers

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.

like image 135
Jay Avatar answered Nov 11 '22 19:11

Jay