[ PHPUnit ] ボーリングから考えるテスト駆動開発 11テスト目

これからはじめるTDD テスト駆動開発入門 ThinkIT Books より

# ボーリングの点数計算プログラムを作る

## Flameクラスの組み込み

### Bonusの管理をFlameに移植し、テストが通るようにする

__BowlingGameで管理しているボーナス変数__

```php
    /** @var bool **/
    private $isSpare;

    /** @var int **/
    private $strikeBonusCount;

    /** @var int **/
    private $doubleBonusCount;
```

__Flameクラスを拡張する__

- ボーナス点を記録するためのaddBonus()を追加
- ボーナスを受け取る回数を管理するneedBonus()を追加

__テスト__

```php:Flame.php
    /**
     * @test
     */
    public function _スペア時のボーナス点加算()
    {
        $this->Flame->recordShot(5);
        $this->Flame->recordShot(5);
        $this->Flame->addBonus(5);
        $this->assertEquals(15, $this->Flame->getScore());
    }
```

__テスト実行__

```bash
1) FlameTest::_スペア時のボーナス点加算
Error: Call to undefined method App\Flame::addBonus()

/var/www/html/test/testDrivenBowling/tests/FlameTest.php:90

ERRORS!
Tests: 18, Assertions: 28, Errors: 1.
```

__ロジック修正__

```php:Flame.php
    /** @var  int */
    private $bonus=0;

    /**
     * @param int $pin
     */
    public function addBonus(int $pin)
    {
        $this->bonus += $pin;
    }

    /**
     * @return int
     */
    public function getScore()
    {
        return $this->score + $this->bonus;
    }
```

__テスト実行__

```bash
OK (18 tests, 29 assertions)
```

### BowlingGameに組み込む

BowlingGameが管理しているフレームにボーナスを足すときには
ボーナス対象のフレームを知っていなければならないことに注意

あくまで、ボーナス点はボーナスを出したフレームではなく
次のフレームの点数によって決まるから

__テスト__

```php:BowlingGameTest.php
    /**
     * スペア時の計算
     *
     * 1投目: 3
     * 2投目: 7  //スペア
     * 3投目: 4
     *
     * 4投目以降、ガター
     *
     *
     */
    public function testSpareCase()
    {
        $this->BowlingGame->recordShot(3);
        $this->BowlingGame->recordShot(7);
        /** ↑ スペア **/

        $this->BowlingGame->recordShot(4);
        $this->loopRecordShot(17, 0);

        $this->assertEquals(18, $this->BowlingGame->calculateScore());
        
        /*
         * スペアのテスト時にフレームの点数がボーナス込みで
         * 14点であることを確認
         */
        $this->assertEquals(14, $this->BowlingGame->flameScore(0));
    }
```

__テスト実行__

```bash
1) BowlingGameTest::testSpareCase
Failed asserting that 10 matches expected 14.

/var/www/html/test/testDrivenBowling/tests/BowlingGameTest.php:61

FAILURES!
Tests: 18, Assertions: 30, Failures: 1.

```

__ロジック修正__

```php:BowlingGame.php
    /** @var  int スペアのフレームを記録 */
    private $spareFlameNo;

    /**
     * スペアの場合、もう一度ピンの数を足して
     * フラグを下げる
     * @param int $pin
     * @return void [description]
     */
    private function calculateSpare(int $pin)
    {
        if ($this->isSpare) {
            //スペアのボーナスをフレーム側に加算
            $this->Flames[$this->spareFlameNo]->addBonus($pin);
            $this->spareFlameNo = null;
            
            $this->score += $pin;
            $this->isSpare = false;
        }
    }

    /**
     * 2投目のときにスペアかどうか判定
     * 10点とったらフラグを立てる
     *
     * @return void      [description]
     */
    private function isSpare()
    {
        if ($this->Flames[count($this->Flames)-1]->isSpare()) {
            $this->isSpare = true;
            
            //スペアをとったフレーム番号を記憶
            $this->spareFlameNo = count($this->Flames)-1;
        }
    }
```

__テスト実行__

```bash
OK (18 tests, 30 assertions)
```

__同様にストライクの場合も組み込む__

__テスト__

```php:BowlingGameTest.php
    /**
     * 初球ストライクのケース
     *
     * 1投目: ストライク
     * 2投目: 3
     * 3投目: 3
     * 4投目: 1
     *
     * 残りガター
     *
     */
    public function testStrikeAtFirstFlame()
    {
        $this->BowlingGame->recordShot(10);
        $this->BowlingGame->recordShot(3);
        $this->BowlingGame->recordShot(3);
        $this->BowlingGame->recordShot(1);

        $this->loopRecordShot(15, 0);

        $this->assertEquals(23, $this->BowlingGame->calculateScore());

        //ストライク時に1フレーム目の点数が16点になっているか
        $this->assertEquals(16, $this->BowlingGame->flameScore(0));
    }
```

__テスト実行__

```bash
1) BowlingGameTest::testStrikeAtFirstFlame
Failed asserting that 10 matches expected 16.

/var/www/html/test/testDrivenBowling/tests/BowlingGameTest.php:116

FAILURES!
Tests: 18, Assertions: 31, Failures: 1.

```

__ロジック修正__

```php:BowlingGame.php
    /** @var  int スペアのフレームを記録 */
    private $strikeFlameNo;

    /**
     * ストライク時の得点計算
     * @param  int    $pin [description]
     * @return void      [description]
     */
    private function calculateStrikeBonus(int $pin)
    {
        if ($this->strikeBonusCount > 0) {
            $this->Flames[$this->strikeFlameNo]->addBonus($pin);
            $this->score += $pin;
            --$this->strikeBonusCount;
        }
    }

    /**
     * ストライク時にボーナス管理の変数をセット
     * @return void      [description]
     */
    private function isStrike()
    {
        if ($this->Flames[count($this->Flames)-1]->isStrike()) {

            $this->strikeFlameNo = count($this->Flames)-1;

            if ($this->strikeBonusCount !== 0) {
                $this->doubleBonusCount = 2;
            }

            if ($this->strikeBonusCount === 0) {
                $this->strikeBonusCount = 2;
            }
        }
    }


```

__テスト実行__

```bash
OK (18 tests, 31 assertions)
```

一旦終わり、次回はダブルストライクとターキーを検討