5

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

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

AnEmptyStack.isNoLongerEmptyAfterPush

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

“给定一个空栈,当它被压入时,它就不再是空的了。”

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

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

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

class WhenAnEmptyStackIsPushed(){

   setup(){
      stack.push();
   }

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

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

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

所以,我正在寻找想法。一种方法优于另一种方法吗?

4

2 回答 2

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

    }
}

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

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

于 2010-01-29T03:10:42.083 回答
0

我认为您的替代方案 2 是可取的。在我看来,每个测试类都应该将 SUT 设置为一种状态,然后每个测试方法都是对该对象的观察。我认为如果你在课堂上添加更多的观察结果会更有意义。如果每个观察都是唯一的,并且没有其他操作的观察,我想你会看到所有观察如何自然地属于一起。

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

于 2009-12-12T05:32:23.797 回答