BDD和“何时”的位置

时间:2009-11-19 00:28:07

标签: bdd

我已经看到了我看来是BDD的两种方法。差异取决于“何时”的位置:

在方法1中,when是规范的一部分:

AnEmptyStack.isNoLongerEmptyAfterPush

在纯粹的“当时给出”术语中,这是:

“给定一个空堆栈,当它被推送时,它就不再是空的。”

所以“when”是规范方法的一部分:

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

在方法2中,when在类级别定义。也就是说,通常在设置中调用when。

class WhenAnEmptyStackIsPushed(){

   setup(){
      stack.push();
   }

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

有首选方法吗?就纯粹的行为测试而言,第二种选择对我来说似乎更为可取,因为测试夹具的重点在于行为。

然而,为了便于测试,我倾向于第一种方法。我在测试中发现的大部分痛苦都是设置。也就是说,我必须在特定状态下获得SUT。一旦进入该状态,通常只需要一行代码来实际调用它上面的某些行为。因此,每个类具有多个行为(即,根据设置上下文)利用该类的一次性设置。

所以,我正在寻找想法。一种方法比另一种方法更受欢迎吗?

2 个答案:

答案 0 :(得分:2)

根据您的测试框架,您可能拥有两全其美的优势。

当我围绕sut创建一组测试时,我首先声明一个将包装整个规范集的类,然后是一个抽象类:

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

    }
}

这只是为了说明测试类通常可以嵌套,因此您仍然可以执行“大”操作...并且当您需要更大的上下文特异性时,通过继承来重用您之前设置的状态。当然,您必须确保您的框架将重置断言集之间的设置。

顺便说一下,我在这里展示的整个委托语法来自Jean-Paul Boodhoo的DevelopWithPassion.Bdd库,你可以在Github上找到它。

答案 1 :(得分:0)

我认为你的替代品2更可取。在我看来,每个测试类应该在一个状态下设置SUT,然后每个测试方法就是对该对象的观察。我认为如果你在课堂上增加一些观察结果会更有意义。如果每次观察都是观察而没有额外行动,我认为你会看到所有观察结果自然是如何组合在一起的。

如果你选择替代1,你不会对观察进行分组,因为它们会观察同一个对象(状态)的不同方面,而是因为它们有一些你想要重用的常见初始状态。不要将测试分组以重用代码,将测试分组,因为它们属于一起并使用其他方法将代码重用为辅助类/方法甚至继承(即所有与堆栈相关的类都可以从创建空堆栈的类继承)。 p>