5

私は、BDD への 2 つのアプローチのように思われることを見てきました。違いは、「いつ」の場所にかかっています。

アプローチ 1 では、when は仕様の一部です。

AnEmptyStack.isNoLongerEmptyAfterPush

純粋な「その時与えられた」用語では、これは次のとおりです。

「空のスタックが与えられた場合、それがプッシュされると、空ではなくなります。」

したがって、「いつ」は指定方法の一部です。

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

アプローチ 2 では、when はクラス レベルで定義されます。つまり、 when は通常、セットアップで呼び出されます。

class WhenAnEmptyStackIsPushed(){

   setup(){
      stack.push();
   }

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

好ましい方法はありますか?動作の純粋なテストに関しては、テスト フィクスチャの焦点が動作にあるため、2 番目のオプションが望ましいと思われます。

ただし、テストを簡単にするために、最初の方法に傾いています。私がテストで見つけた苦労のほとんどはセットアップです。つまり、特定の状態で SUT を取得する必要があります。その状態になると、通常、実際に何らかの動作を呼び出すコードは 1 行だけです。そのため、クラスごと (つまり、セットアップ コンテキストごと) に複数の動作を設定すると、クラスの 1 回限りのセットアップが活用されます。

だから、私は考えを探しています。1 つのアプローチが他のアプローチよりも優先されますか?

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 ...を実行し、コンテキストの特異性を高める必要がある場合は継承することで、以前に設定した状態を再利用できます。もちろん、フレームワークがアサーションのセット間でセットアップをリセットすることを確認する必要があります。

ちなみに、ここで示しているデリゲート構文全体は、GithubにあるJean-PaulBoodhooのDevelopWithPassion.Bddライブラリからのものです。

于 2010-01-29T03:10:42.083 に答える
0

あなたの選択肢2が好ましいと思います。私の意見では、各テスト クラスは SUT を 1 つの状態にセットアップする必要があり、各テスト メソッドはそのオブジェクトに対する観察です。クラスにさらに多くの観測を追加すると、より理にかなっていると思います。それぞれの観察が唯一であり、追加のアクションを伴わない観察である場合、すべての観察がどのように自然に一緒に属しているかがわかると思います。

代替案 1 を使用する場合、観測をグループ化するのは、同じオブジェクト (状態) のさまざまな側面を観測するためではなく、再利用したい共通の初期状態があるためです。コードを再利用するためにテストをグループ化しないでください。テストをグループ化するのは、それらが一緒に属し、他の手段を使用してコードをヘルパー クラス/メソッドまたは継承として再利用するためです (つまり、スタックに関連するすべてのクラスは、空のスタックを作成するクラスから継承できます)。

于 2009-12-12T05:32:23.797 に答える