3

私はTDDをフォロー/学習しようとしています。私はPHPUnitを使用しています。現時点では、すべてを取り入れるためだけに、小さくて非常に単純なクラス/プロジェクトを作成しています。

現在、プレイカードのセットアップを書いています。

それを始めるために、私はこのテストを書きました。カード スーツでの繰り返しを避けるため、このテストは、スーツが拡張できる抽象クラス用にすべきであると判断しました。

クラスのパターンは、投稿の最後にたどり着いたときに明らかになることを願っています。

BaseSuitTest.php

use Cards\French\Suits\BaseSuit as Card;

class BaseSuitTest extends PHPUnit_Framework_TestCase 
{

    protected $Card;

    public function setUp()
    {
        $this->Card = $this->getMockForAbstractClass('Cards\French\Suits\BaseSuit', [10]);
    }


    /**
    *   @expectedException InvalidArgumentException
    */
    public function testConstructThrowsExceptionIfFirstArgLessThan1()
    {
        $Card = $this->getMockForAbstractClass('Cards\French\Suits\BaseSuit', [0]);
    }


    /**
    *   @expectedException InvalidArgumentException
    */
    public function testConstructThrowsExceptionIfFirstArgHigherThan13()
    {
        $Card = $this->getMockForAbstractClass('Cards\French\Suits\BaseSuit', [14]);
    }


    public function testGetSuitReturnsClassName() 
    {
        $suit = $this->Card->getSuit();
        $this->assertSame( get_class($this->Card), $suit);
    }


    public function testGetValueReturnsValue()
    {
        $value = $this->Card->getValue();
        $this->assertSame(10, $value);
    }
}

その後、次のファイルを作成し、すべてのテストに合格しました。この時点で、CardInterface を BaseSuit に実装する必要があるかどうかも考えています (実装を確認するテストを作成する必要があるか、これはテストの一部ではありません)。私はしないことにしました。

BaseSuit.php

namespace Cards\French\Suits;

abstract class BaseSuit 
{

    const MIN_VALUE = 1;
    const MAX_VALUE = 13;


    protected $value;


    public function __construct($value)
    {
        if( $this->isWithinValueRange($value) === false)
            throw new \InvalidArgumentException('The value must be higer than 0 and less than 14 (1-13) Given ' . $value);
        $this->value = $value;
    }


    protected function isWithinValueRange($value)
    {
        if($value < self::MIN_VALUE OR $value > self::MAX_VALUE)
            return false;
        return true;
    }


    public function getSuit()
    {
        return get_class($this);
    }


    public function getValue()
    {
         return $this->value;
    }
}

最後に、基本クラスから拡張される実際のクラス/スーツのテストを開始しました。

このテストは BaseSuit のテストと非常によく似ており、すでに頭の中で私を悩ませています。

HeartTest.php

use Cards\French\Suits\Heart as Heart;

class HeartTest extends PHPUnit_Framework_TestCase 
{

    protected $Heart;


    public function setUp()
    {
        $this->Heart = new Heart(10);
    }


    /**
    *   @expectedException InvalidArgumentException
    */
    public function testConstructThrowsExceptionIfFirstArgLessThan1()
    {
        $Heart = new Heart(0);
    }


    /**
    *   @expectedException InvalidArgumentException
    */
    public function testConstructThrowsExceptionIfFirstArgHigherThan13()
    {
        $Heart = new Heart(14);
    }


    public function testGetSuitReturnsClassName() 
    {
        $suit = $this->Heart->getSuit();
        $this->assertSame( get_class($this->Heart), $suit);
    }


    public function testGetValueReturnsValue()
    {
        $value = $this->Heart->getValue();
        $this->assertSame(10, $value);
    }
}

テストに合格するように、もう一度コードを書きます。

Heart.php

namespace Cards\French\Suits;

class Heart extends BaseSuit {}

すべてのテストに合格しますが、すべてがクールというわけではありません。BaseSuit から拡張されたスーツ (ハートなど) のテストでの繰り返しは避けたいと思います。

私の最初の考えは、BaseSuit のメソッドの名前を protected に変更し、このテストから拡張することでした。名前が変更された BaseSuit テストの 1 つの例を次に示します。

BaseSuit.php のリファクタリングされたメソッド

/**
*   @expectedException InvalidArgumentException
*/
protected function constructThrowsExceptionIfFirstArgLessThan1($class)
{
    // .. i have not written this code, it just a figment of my imagination.
}

ここで分けました。継続するか迷っています。

BaseSuitTest 内のテストを保護されたメソッドに移動すると、現在のパブリック テスト メソッドが失われることになります。これは、保護されたメソッドを呼び出す新しいテスト メソッドを作成することで修正できます。

コード例、リファクタリングの提案、パターンの提案、およびこれを読んでいるときに頭の中に建設的なものが浮かんでくると、非常に感謝しています。

同じメソッドで実行されるテストにはプロバイダーを使用することをお勧めします。スマート、私はこれを将来行うつもりですが、これが私の状況をどのように解決できるかわかりません. 抽象 BaseSuit の場合は、テストで getMockForAbstract を使用したいだけで、スーツの場合は、HeartTest のようにする必要があります。これを正しい方法で実装する方法を理解する助けが必要です。

4

1 に答える 1

2

TDDの主な目標を見逃していると思います。コードと作業の重複を避けるためにテストをリファクタリングする方法を尋ねる代わりに、テストを使用して非テスト コードの設計を通知する必要があります。なぜHeart独自のクラスなのですか?、、および とBaseSuitはどのような動作を変更または追加しますか?SpadeDiamondClub

カードの序数値はすでにデータ属性になっています。スーツでも同じことをする必要があります。各カードが値を持つスーツである代わりに、スーツと値を持つ必要があります。Cardこれにより、基本バージョンまたは抽象バージョンを必要としない、単一の具体的なクラスを持つことができます。Suits定数と順序付けに必要なメソッドを使用して、さまざまなスーツのを定義するクラスを作成します(ただし、これらはおそらくクラスに属しますRules)。

それでもスーツをより複雑にモデル化する必要がある場合は、、、 などをデータ属性としてSuit持つ単一のジェネリック クラスに固執することをお勧めします。namesymbolorder

于 2013-10-05T18:10:23.040 に答える