私は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 のようにする必要があります。これを正しい方法で実装する方法を理解する助けが必要です。