2

私はmspecが大好きです。技術者ではない人々と簡単にコミュニケーションできる方法で重要な例を提供するのに最適ですが、不必要な冗長性、特にクラスの爆発を提供する場合があります。

次の例を見てください。

チェスの騎士の駒の動きをモデル化したいと思っています。騎士が他の駒やボードの境界の近くにないと仮定すると、騎士が持つことができる 8 つの可能な動きがあります。これらの可能性のそれぞれをカバーしたいのですが、率直に言って、8 つの個別の仕様 (8 つのクラス) を書くのが面倒です。私はビヘイビアと継承で賢くなれることを知っていますが、8つの有効な動きをカバーしたいので、8つの s なしでそれを行う方法がわからないbecauseため、8つの個別のクラスになります。

これらのシナリオを mspec でカバーする最善の方法は何ですか?

いくつかのコード。

public class Knight
{
    public string Position {get; private set;}

    public Knight(string startposition)
    {
         Position = startposition;
    }

    public void Move
    {
          // some logic in here that allows a valid move pattern and sets positions
    }


}

私がするかもしれないこと。

[Subject(typeof(Knight),"Valid movement")]
public class when_moving_the_knight
{
     Establish that = () => knight =new Knight("D4");
     Because of = ()=> knight.Move("B3");
     It should_update_position = ()=> knight.Position.ShouldEqual("B3");
     It should_not_throw;
     /// etc..    
} 

しかし、8回ではありません。

4

3 に答える 3

3

正直なところ、MSpec でこれを行うための最良の方法を説明できませんでした。しかし、同様の状況で MSpec を使用すると、同様のクラス爆発の問題が発生しました。RSpec を試したことがあるかどうかはわかりません。RSpec では、コンテキストと仕様は実行可能コードの範囲内で構築されます。つまり、データ構造を作成し、それを繰り返し、1 つのコード ブロックを使用して複数のコンテキストと仕様を作成できるということです。これは、数学に基づくもの (素因数、三目並べ、チェスなど) がどのように動作するかを指定しようとしている場合に特に便利です。指定された値と期待される値のセットの各メンバーに対して、単一の動作パターンを指定できます。

この例は、RSpec をモデルにした C# のコンテキスト/スペック フレームワークである NSpec で記述されています。私は故意に失敗した仕様を残しました。繰り返しを使用する場所を見つけるのに十分なだけ、この型を下に移動しました。失敗した仕様により、単純な実装の欠点を解決する必要があります。

素因数カタの別の例を次に示します: http://nspec.org/#dolambda

出力:

describe Knight
  when moving 2 back and 1 left
    when a knight at D4 is moved to B3
      knight position should be B3
    when a knight at C4 is moved to A3
      knight position should be A3 - FAILED - String lengths are both 2. Strings differ at index 0., Expected: "A3", But was: "B3", -----------^

**** FAILURES ****

describe Knight. when moving 2 back and 1 left. when a knight at C4 is moved to A3. knight position should be A3.
String lengths are both 2. Strings differ at index 0., Expected: "A3", But was: "B3", -----------^
   at ChessSpecs.describe_Knight.<>c__DisplayClass5.<when_moving_2_back_and_1_left>b__4() in c:\Users\matt\Documents\Visual Studio 2010\Projects\ChessSpecs\ChessSpecs\describe_Knight.cs:line 23

2 Examples, 1 Failed, 0 Pending

コード:

using System.Collections.Generic;
using NSpec;

class describe_Knight : nspec
{
    void when_moving_2_back_and_1_left()
    {
        new Each<string,string> { 
            {"D4", "B3"},
            {"C4", "A3"},
        }.Do( (start, moveTo) =>
        {
            context["when a knight at {0} is moved to {1}".With(start,moveTo)] = () =>
            {
                before = () =>
                {
                    knight = new Knight(start);
                    knight.Move(moveTo);
                };
                it["knight position should be {0}".With(moveTo)] = () => knight.Position.should_be(moveTo);
            };
        });
    }
    Knight knight;
}

class Knight
{
    public Knight(string position)
    {
        Position = position;
    }

    public void Move(string position)
    {
        Position = "B3";
    }

    public string Position { get; set; }
}
于 2011-04-30T02:58:40.913 に答える
1

好きなように使用してください。ここからあちらへ、ここからあちらへ(2) へ、などと移動できるはずです。rspec では非常に一般的なパターンですが、MSpec ではそれほど多くはありません。間違った方向に導くことを恐れて、それについて。しかし、これはこれを使用するのに最適な場所です。あなたは騎士の動きの振る舞いを説明しています。

その内容をより具体的にすることで、より適切に説明することができます。上に 2 つ、右に移動できる必要があります。上に 2 つ、左に移動できる必要があります。味方の駒などの上に移動することはできません。

はい、It に 1 行以上のコードを記述する必要がありますが、問題ありません。少なくとも私の意見では。

于 2011-04-27T02:56:29.773 に答える
1

あなたのデザインを見ると、ナイトが無効な位置に移動すると例外がスローされることがわかります。この場合、あなたのメソッドには 2 つの異なる責任があると思います。1 つは有効な動きをチェックするため、もう 1 つは正しい動きまたは投げを行うためです。メソッドを2つの異なる責任に分割することをお勧めします。

この特定のケースでは、移動が有効かどうかを確認するメソッドを抽出し、移動メソッドから呼び出します。そんな感じ:

public class Knight
{
    internal bool CanMove(string position)
    {
        // Positioning logic here which returns true or false
    }

    public void Move(string position)
    {
        if(CanMove(position))
            // Actual code for move
        else
            // Throw an exception or whatever
    }
}

このようにして、CanMove 内のロジックをテストして、特定の Knight の有効な位置をテストし (単一のテスト クラスと異なる "It" で実行できます)、Move メソッドのテストを 1 つだけ実行して、いつ失敗するかを確認します。無効な位置が与えられました。

于 2011-04-27T07:18:45.197 に答える