実践TDDの点滴——前置条件の準備
1本の関数の複数の前置条件は、論理コードが順番に記述されているため、これらの条件が別々にテストされると、第2の条件テストが存在する場合、第1の条件が成立することを確保し(MOCK方式でも煩わしい)、第4の条件は前の3つの条件が成立する必要があり、前置条件が多ければ多いほど煩わしいので、この問題をどのように解決するか.
例:プレイヤーがゲームの世界に入るロジック.
(C++の文法、私はできるだけ簡略化して書いて、みんなは理解するべきです)
1、ゲームの世界人数が1000を超えると、失敗に入る.
2、PlayerNameが見つからない、つまりPlayerが作成されていない場合は失敗します.
3、プレイヤーがいるシーンがゲームワールドに追加されていない場合は失敗します.
4、プレイヤーが既にゲームの世界に入っている場合は、失敗します.
対応するクラスおよびメンバー関数は、以下のように簡略化されています.
4番目の論理にテストコードを書きます.つまり、プレイヤーがゲームの世界に入った場合、失敗します.
1つの書き方は次のとおりです.
このような書き方は、初めて世界に入るのに成功することを要求しているが、世界に入るには他の3つの条件を満たす必要がある.1番目の条件は比較的満足しやすいが、2番目の条件はmockによって「jack」が作成されていることを確認し、3番目の条件はgameWorldにjackが存在するシーンを追加する必要がある.を選択して設定できます.そうすると、このテストの準備はたくさんあります.多くは,以前に第1,2,3論理を記述したときに既に書かれたコードである.テストの独立性を満たすために、私の4つの論理のテストコードは分かれていて、これで重複するコードが現れました.
2つ目の書き方:
gameWorldに直接アクセスするプライベート変数m_PlayerExistsでは、「jack」というPlayerオブジェクトを追加します.
この書き方が最も簡単で,特にc++ではテストクラスをGameWorldのfriendにするだけでプライベート変数にアクセスできる.しかし、このようなやり方はクラスのパッケージを深刻に破壊し、既存のプレイヤーがlistで保存されず、mapで保存されるように再構成されていない場合、テストコードも変更されなければならない.照理クラス内部のコード再構築はテストコードに影響を与えるべきではない.
3つ目の書き方:
GameWorldにisPlayerIn()の関数を追加
そしてgameWorldに対してmockを行います.
この書き方は2つ目よりもよく、extract methodも駆動されますが、同じように不適切な点もあります.isPlayerInは実際のコードでは使われていません.privateのはずですが、テストコードmockのためにprotectedに変更します.もちろんisPlayerInをpublicに開いてもいいと言いますが、enterPlayerの後置検出条件としてちょうどいいです.ここで私も同じように困惑しています.テスト中の後置検出条件はget関数で提供する必要がありますが、テストコードでしか使用されず、実際のコードでは呼び出されません.このような検出関数は多く,publicであればクラスのインタフェース定義は膨大である.クラスのpublic関数が少なければ少ないほど、不要なものはできるだけ提供しないことを提唱してきました.これにより、呼び出し者は呼び出しやすくなります.
実は複数の前置条件がこの問題に遭遇するだけでなく、多くの追加、削除機能がこの問題に直面します.オブジェクトの論理を削除すると、追加するすべての準備が完了し、結果として追加部分のコードが繰り返しテストされます.私のプロジェクトでは、チームメンバーからすべてのテストを一緒に書いて、中間論理に追加してから削除します.その結果、100行以上のテストが行われ、読みにくく、修理しにくい.
例:プレイヤーがゲームの世界に入るロジック.
(C++の文法、私はできるだけ簡略化して書いて、みんなは理解するべきです)
1、ゲームの世界人数が1000を超えると、失敗に入る.
2、PlayerNameが見つからない、つまりPlayerが作成されていない場合は失敗します.
3、プレイヤーがいるシーンがゲームワールドに追加されていない場合は失敗します.
4、プレイヤーが既にゲームの世界に入っている場合は、失敗します.
対応するクラスおよびメンバー関数は、以下のように簡略化されています.
class GameWorld
{
public:
bool enterPlayer(String playerName)
{
}
private:
list<Player> m_playerExists;
};
4番目の論理にテストコードを書きます.つまり、プレイヤーがゲームの世界に入った場合、失敗します.
1つの書き方は次のとおりです.
gameWorld.enterPlayer(“jack”) ;
CPPUNIT_ASSERT( !gameWorld.enterPlayer(“jack”) );
このような書き方は、初めて世界に入るのに成功することを要求しているが、世界に入るには他の3つの条件を満たす必要がある.1番目の条件は比較的満足しやすいが、2番目の条件はmockによって「jack」が作成されていることを確認し、3番目の条件はgameWorldにjackが存在するシーンを追加する必要がある.を選択して設定できます.そうすると、このテストの準備はたくさんあります.多くは,以前に第1,2,3論理を記述したときに既に書かれたコードである.テストの独立性を満たすために、私の4つの論理のテストコードは分かれていて、これで重複するコードが現れました.
2つ目の書き方:
gameWorldに直接アクセスするプライベート変数m_PlayerExistsでは、「jack」というPlayerオブジェクトを追加します.
この書き方が最も簡単で,特にc++ではテストクラスをGameWorldのfriendにするだけでプライベート変数にアクセスできる.しかし、このようなやり方はクラスのパッケージを深刻に破壊し、既存のプレイヤーがlist
3つ目の書き方:
GameWorldにisPlayerIn()の関数を追加
virtual bool isPlayerIn(String playerName)
{
}
そしてgameWorldに対してmockを行います.
class GameWorldMock : public GameWorld
{
protected:
virtual bool isPlayerIn(String playerName)
{
return true;
}
};
この書き方は2つ目よりもよく、extract methodも駆動されますが、同じように不適切な点もあります.isPlayerInは実際のコードでは使われていません.privateのはずですが、テストコードmockのためにprotectedに変更します.もちろんisPlayerInをpublicに開いてもいいと言いますが、enterPlayerの後置検出条件としてちょうどいいです.ここで私も同じように困惑しています.テスト中の後置検出条件はget関数で提供する必要がありますが、テストコードでしか使用されず、実際のコードでは呼び出されません.このような検出関数は多く,publicであればクラスのインタフェース定義は膨大である.クラスのpublic関数が少なければ少ないほど、不要なものはできるだけ提供しないことを提唱してきました.これにより、呼び出し者は呼び出しやすくなります.
実は複数の前置条件がこの問題に遭遇するだけでなく、多くの追加、削除機能がこの問題に直面します.オブジェクトの論理を削除すると、追加するすべての準備が完了し、結果として追加部分のコードが繰り返しテストされます.私のプロジェクトでは、チームメンバーからすべてのテストを一緒に書いて、中間論理に追加してから削除します.その結果、100行以上のテストが行われ、読みにくく、修理しにくい.