2017-04-15 12 views
2

私はボードを2つのフォーマット、すなわちlong positionIDbyte[][] boardで表すミニチェッカーを書いた。以前は保存するのが安く、後者を表現/操作する方が簡単です。変換自体は簡単です(下のコードを参照)。テスト駆動開発。メソッドが作成される前に、この変換の単体テストを書く方法は?

TDDは「1つのテストを書き留めてから、実動コードを書く」と述べています。表現変換でこれをどのように行うべきですか?
assertEquals(0L, toIndex(new byte[6][6]))のようなユニットテストは、それほどのカバレッジを提供しません。 Long myPosID = 42L; assertEquals(myPosID, toIndex(toBoard(myPosID))のテストでは、大きな価値はありません。テスト全体の範囲は永遠にかかるでしょう。いくつかのランダムなmyPosID値(モンテカルロシミュレーション)の単体テストを実行する方が良いようですが、合格テストでさえ意味はありません。

TDDでどのように行う必要がありますか?

/* 
    This class manipulates checkers board representation. Position is stored as long and represented as byte[height][width] board. 
    For board representation white = 0, black = 1, empty = 2. 

    Long positionID to byte[][] board: 
    get ternary numeral system representation of positionID, place positional values to corresponding squares. 

    For conversion from byte[][] board to long positionID: 
    long positionID = 0; for each (byte playableSquare : board){playable square positionID = positionID*3. positionID+= playableSquare} 
    */ 

    public final int boardHeight = 6; 
    public final int boardWidth = 6; 



    public long toIndex(byte[][] board) { 
     byte coords[] = new byte[boardHeight * boardWidth/2]; 
     int totalSquares = boardHeight * boardWidth/2; 
     byte k = 0; 
     for (int i = 0; i < boardHeight; i++) { 
      for (int j = 0; j < boardWidth/2; j++) { 
       byte makeItCheckers = (byte) ((i + 1) % 2); 
       coords[k] = board[i][j * 2 + makeItCheckers]; 
       k++; 
      } 
     } 
     long positionID = 0; 
     for (int i = totalSquares - 1; i >= 0; i--) { 
      positionID = positionID * 3 + coords[i]; 
     } 
     return positionID; 
    } 



    public byte[][] toBoard(long positionID) { 
     int totalSquares = boardHeight * boardWidth/2; 

     int[] coords = new int[totalSquares]; 

     for (int i = 0; i < totalSquares; i++) { 

      coords[i] = (int) (positionID % 3L); 
      positionID = positionID/3L; 
     } 
     byte[][] board = new byte[boardHeight][boardWidth]; 
     Arrays.fill(board, 2); 
     byte k = 0; 
     for (int i = 0; i < boardHeight; i++) { 
      for (int j = 0; j < boardWidth/2; j++) { 
       byte makeItCheckers = (byte) ((i + 1) % 2); 
       board[i][j * 2 + makeItCheckers] = (byte) coords[k]; 
       k++; 
      } 
     } 
     return board; 
    } 

答えて

2

TDDは実装の前にテストを書いています。
あなたは逆のやり方をしているようです。

TDD、さらに一般的には変換処理の単体テストを書くには、受け入れテストの観点から考える必要があります。
変換処理のシナリオを特定する必要があります。
あなたは入力として持ち、出力として何を期待していますか?


テスト範囲全体では、シナリオの数百あるいは数千を持っている場合、これらは、実装が長くなりますので、あなたがそれらのすべてをテストするべきではありません、確かに永遠に

がかかりますユニットテストはを実行するには長すぎるになることがあります。
ユニットテストの原則に反します。
ユニットテストは非常に頻繁に実行されるため、高速で実行する必要があります。いくつかのランダムmyPosID値のためのユニットテスト(モンテカルロ シミュレーション)を実行している

は良さそうですが、その後もテストに合格することは あまり意味がありません。あなたはが、これらは再現できない場合がありますので、テストが実行されるたびに異なって生成されたランダム系列 を使用しないでください示唆としてランダムな値

テスト。
また、単体テストの原則に反します。
単体テストは、任意の環境でいつでも同じ結果を生成する必要があります。
それ以外の場合は、テストが信頼できないことを意味します。


ので、単体テストを作成するためのアイデアは、TDDの方法である処理する例タイプなどなど、多くのユニットテストを書いています。例えば

:これらは3つの受け入れテストを作成することができる

、1 =黒、0 =白空= 2

:あなたはセルを表すの3つの方法を持っていますLongからbyte[][]への変換の場合には、逆である。

1)私はLong値、1つの、白セルと空として持っていた場合、私はLong値、唯一の空のセルとして、私は)

2 ...としてバイト配列表現を待っていています残りのセルのように、私はバイト配列の表現を待っています... Longの値、1黒セルと残りの空の値として持っているとき、私はバイト配列の表現を待っています...

あなたはもっと遠くに行くことができます。
白と黒のセルを混ぜ合わせることで副作用が生じないことを確認する受け入れテストを作成してみませんか?

4)私は残りのLong値、3個の白血球、4つの黒セルと空のセルとして持っていた場合、私は、最後に...

としてバイト配列表現を待っていますあなたがすべてのケースをテストすべきかどうかという疑問については、上記のように「大きなケース」に集中しようとするべきだと思います。
いいですね。

+0

はい、TDDでは、最初に失敗したテストを書き、次にテストに合格するプロダクションコードを書きます。私は最初に自分のコードを書いた。今私はTDDのように見えるだろうと思っています。彼らは最初に変換機能やテストを持っていなかったでしょう。彼らはテストを書くだろう。テストはどのように見えますか? – Stepan

+0

TDDを行う場合、必要とするすべての処理を事前に知っているわけではありません。アプリケーションには、まずビジネス要件がTDDでコード化されます。ノーマル:最初のビジネスニーズから始め、テストの作成と実装を始めます。開発中は、より細かいニーズや技術的な要件が発生します(ここでは、データをフォーマットから別のフォーマットに変換します)、最初のビジネスニーズを満たすにはそれを取得する必要があります。だからあなたはTDDでそれらを指定します。 – davidxxx

+0

この論理を私の質問に適用します。私はTESTを必要とし、後で、 'Long positionID'の十進数表現から三項数値システムに行く方法を" for positionID'の三進数表現で対応する位置の各四角値に割り当てます。このテストを書くには?それは何もカバーしていません(0L、1L、2Lをチェックし、 "私たちは合格!"と言っています - テストとトリッキーな大学を意味しませんでした)。メソッド自体は簡単です。しかし、テストがとても難しい場合は、TDDのポイントは何ですか? – Stepan

1

競争のプログラミングでも同様の問題があります。コードを送信すると、可能なすべての入力を渡していないため、システムはコードの100%の正当性を検証できません。代わりに、システムは、多くのテストを実行する三つのカテゴリーに入るよう:

  • コーナー例:空の入力、例えば
  • 非常に大きい場合
  • いくつかの一般的な例は、だからあなたをもパフォーマンス

をテストしますこのtechinqueにも従うことができますが、スケールに合わせて、あなたに合っています。

また、正規のTDDは式のようなメソッドでは機能しません。なぜなら、常に別のifでテストに合格できるからです。代わりに、私たちはテストが正しいアルゴリズムの実装だけでなく正しい設計を提供するという事実に焦点を当てるべきです。

1

TDDで式を確認するのは難しいです。あなたはモンテカルロの変種を使うことができます。 1000(または100 000)個の乱数Long testIDを生成します。それらをどこかに保存してください。常にこのリストを使って変換を確認してください。 IDはランダムになりますが、テストごとに変更されることはありません。このようにして、「テストは同じ結果を出す必要があります」に従います。

TDDは安くて平凡な従業員が多いとうまくいくようです。その後、マネージャはテストを実行することができます(メソッドにテストがない場合は簡単にチェックできます)。他のコーダーが既存のコードを破るパッチを提出することは難しいです(コミットはJUnitテストに合格しません。テストは労働者を減速させますが、テストがマネージャーを遅らせることがない限り、問題はありません。より多くのコーダーを雇うだけです。

これは、プロジェクトが最初から開始しているときに特に効果的です。

コーダーがまともであり、労力がかかり、プロジェクトが成熟した場合、行動主導型のテストではより良い結果が得られます。

関連する問題