BDD and when location,

I saw what seems to me to be two approaches to BDD. The difference depends on the location of the "when":

In approach 1, when it is part of the specification:

AnEmptyStack.isNoLongerEmptyAfterPush 

In pure "given when" terminology, this is:

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

Thus, when is part of the specification method:

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

In approach 2, the value is determined at the class level. That is, when it is usually called in setup.

 class WhenAnEmptyStackIsPushed(){ setup(){ stack.push(); } public void thenItIsNotEmpty(){ assert(stack.notEmpty()) } } 

Is there a preferred method? As for pure behavior testing, the second option seems preferable to me, since the focus of the test device depends on its behavior.

However, for the convenience of testing, I am inclined to the first method. Most of the pain I find in testing is tuning. That is, I have to get the SUT in a certain state. Once in this state there is usually only one line of code to actually cause some kind of behavior on it. Thus, having multiple actions for each class (that is, for each setting context) uses a one-time class setting.

So I'm looking for thoughts. Is one approach preferable to another?

+4
source share
2 answers

Depending on your testing structure, you may have the best of both worlds.

When I create a test suite around the gist, I first declare a class that wraps the entire set of specifications, and 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 just illustrates that test classes can often be nested, so you can still do “big” ones when ... and reuse the state you configured earlier, inheriting when you need more contextual specificity. Of course, you need to be sure that your infrastructure will reset to install between sets of claims.

By the way, all the delegate syntax that I am showing here is from the Jean-Paul Boodhoo DevelopWithPassion.Bdd library, which you can find on Github.

+1
source

I think your alternative 2 is preferable. In my opinion, each test class should configure SUT in one state, and each test method is observations on this object. I think this makes sense if you add a few more notes to the class. If each observation is only an observation without additional actions, I think you will see how all the observations naturally belong to each other.

If you go with alternative 1, you do not group observations because they observe different aspects of the same object (state), but rather because they have a common initial state that you want to reuse. Do not group tests for code reuse, group tests because they belong to each other and use other ways to reuse code as auxiliary classes / methods or even inheritance (i.e. all classes associated with the stack can inherit from a class that creates an empty stack).

0
source

All Articles