これからはじめるTDD テスト駆動開発入門 ThinkIT Books より
ボーリングの点数計算プログラムを作る
フレーム単位でのスコアを取得する
BowlingGameTestに以下の機能を実装。
- フレーム単位でのスコアを取得できるflameScoreメソッドを実装
テストを書く
フレーム毎のスコアはBowlingGameクラスのflameScore($flameNumber)で
取得すると仮定($flameNumberで取得したいフレームの番号を指定)
/**
* 全球ガーター時の1フレーム目の得点
*/
public function testCalculateFrameScoreAtAllGarter()
{
$this->loopRecordShot(20, 0);
$this->assertEquals(0, $this->BowlingGame->flameScore(1));
}
実行して怒られる
1) BowlingGameTest::testCalculateFrameScoreAtAllGarter
Error: Call to undefined method App\BowlingGame::flameScore()
/var/www/html/test/UnitTest/tests/BowlingGameTest.php:213
ERRORS!
Tests: 10, Assertions: 9, Errors: 1.
テストが通るように処理を書く(最小の構成で)
/**
* @return int
*/
public function flameScore(): int
{
return 0;
}
テスト実行
OK (10 tests, 10 assertions)
このままでは、最初のテストケースから実装と同じことを繰り返す・・?
↓
設計の見直し
BowlingGameから、フレーム毎に得点を取得するためのFlameという概念を抽出
BowlingGame ⇔ Flame
機能面の変更
- BowlingGameのcalculateScoreはFlameのscoreを合算して返す
- BowlingGameのrecordShotは現在のFlameのrecordShotを呼び出す
- BowlingGameのflameScoreは指定されたFlameのscoreを返す
Flameクラスで全ての投球がガーターの時のテストを通す
①テストを書く
class FlameTest extends TestCase
{
/** @var Flame */
public $Flame;
public function setUp()
{
$this->Flame = new Flame();
}
public function tearDown()
{
unset($this->Flame);
}
/**
* @test
*/
public function 全ての投球がガーター()
{
$this->Flame->recordShot(0);
$this->Flame->recordShot(0);
$this->assertEquals(0, $this->Flame->getScore());
}
}
日本語でテスト名書くようにしてみた
②テスト実行
1) FlameTest::全ての投球がガーター
Error: Call to undefined method App\Flame::recordShot()
/var/www/html/test/UnitTest/tests/FlameTest.php:25
ERRORS!
Tests: 1, Assertions: 0, Errors: 1.
③テストを通すように処理を書く
class Flame
{
/** @var int */
private $score;
/**
* @param int $pin
*/
public function recordShot(int $pin)
{
}
/**
* @return int
*/
public function getScore()
{
return 0;
}
}
④テスト実行
OK (1 test, 1 assertion)
Flameクラスで全ての投球が1ピンだったときのテストを通す
① テストを書く
/**
* @test
*/
public function 全ての投球で1ピン倒した()
{
$this->Flame->recordShot(1);
$this->Flame->recordShot(1);
$this->assertEquals(2, $this->Flame->getScore());
}
②テスト実行
1) FlameTest::全ての投球で1ピン倒した
Failed asserting that 0 matches expected 2.
/var/www/html/test/UnitTest/tests/FlameTest.php:39
FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
③テストが通るように処理を書く
class Flame
{
/** @var int */
private $score;
/**
* @param int $pin
*/
public function recordShot(int $pin)
{
$this->score += $pin;
}
/**
* @return int
*/
public function getScore()
{
return $this->score;
}
}
④テスト実行
OK (2 tests, 2 assertions)
感想
設計を変えて新しい概念が出てきたが、テスト駆動の基本は変わらないので
今までと同様に小さく積み上げていけば対応はできそう。
細かくリファクタリングを癖にできるようにしたい。