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

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

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

## フレーム単位でのスコアを取得する

### ダブルストライクとターキーの処理を移していく

__テスト__

```php:BowlingGameTest.php
    /**
     * 2球連続ストライクのケース
     *
     * 1投目: ストライク
     * 2投目: ストライク
     * 3投目: 3
     * 4投目: 1
     *
     * 残りガター
     *
     */
    public function testDoubleStrikeAtFirstFlame()
    {
        $this->BowlingGame->recordShot(10); //10+(10+3)
        $this->BowlingGame->recordShot(10); //10+(3+1)
        $this->BowlingGame->recordShot(3); //3
        $this->BowlingGame->recordShot(1); //1           [計41]

        $this->loopRecordShot(14, 0);

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

        //ストライク時に1フレーム目と2フレーム目の点数がただし以下
        $this->assertEquals(23, $this->BowlingGame->flameScore(0));
        $this->assertEquals(14, $this->BowlingGame->flameScore(1));
    }
```

__テスト実行__

```bash
1) BowlingGameTest::testDoubleStrikeAtFirstFlame
Failed asserting that 20 matches expected 23.

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

FAILURES!
Tests: 18, Assertions: 32, Failures: 1.
```

__ロジック修正__

```php:BowlingGame.php
    /** @var  int ダブルストライクのフレームを記録 */
    private $strikeDoubleFlameNo;

    /**
     * ダブル時の得点計算
     * @param  int    $pin [description]
     * @return void      [description]
     */
    private function calculateDoubleBonus(int $pin)
    {
        if ($this->doubleBonusCount > 0) {

            //ダブルの一投目は二つ前のフレームに送る
            if ($this->doubleBonusCount === 2) {
                $this->Flames[$this->strikeDoubleFlameNo-1]->addBonus($pin);
            } else {
                $this->Flames[$this->strikeDoubleFlameNo]->addBonus($pin);
            }

            $this->score += $pin;
            --$this->doubleBonusCount;

        }
    }

/**
     * ストライク時にボーナス管理の変数をセット
     * @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->strikeDoubleFlameNo = count($this->Flames)-1;
                $this->doubleBonusCount = 2;
            }

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

__テスト実行__

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

### オープンフレームの概念を取り込んでおく

* ボーナス計算が発生しないフレームのこと

FlameにneedBonus()を実装し、スペアとストライク以外では
falseを返すようにする

一気にやることにします。
もうそろそろめんどくさくなってきたw


__テスト__

```php
    /**
     * @test
     */
    public function _オープンフレームにボーナスは不要()
    {
        $this->Flame->recordShot(3);
        $this->Flame->recordShot(3);
        $this->assertFalse($this->Flame->needBonus());
    }

    /**
     * @test
     */
    public function _スペアのボーナスは1投分で完了()
    {
        $this->Flame->recordShot(5);
        $this->Flame->recordShot(5);
        $this->assertTrue($this->Flame->needBonus()); //ボーナス付与前
        $this->Flame->addBonus(5);
        $this->assertFalse($this->Flame->needBonus());
    }

    /**
     * @test
     */
    public function _ストライクのボーナスは2投分で完了()
    {
        $this->Flame->recordShot(10);
        $this->Flame->addBonus(5);
        $this->assertTrue($this->Flame->needBonus()); //ボーナス付与後①
        $this->Flame->addBonus(5);
        $this->assertFalse($this->Flame->needBonus()); //ボーナス付与後②
    }
```

これが通るロジックは↓

__ロジック修正__

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

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


    /**
     * @return bool
     */
    public function needBonus(): bool
    {
        if ($this->isSpare()) {
            return $this->bonusCount < 1;
        }

        if ($this->isStrike()) {
            return $this->bonusCount < 2;
        }

        return false;
    }
```

__テスト実行__

```bash
OK (21 tests, 38 assertions)
```

### ゲームの合算値をFlameから返す($scoreプロパティの排除)

これを

```php:BowlingGameTest.php
    /**
     * @return int [description]
     */
    public function calculateScore(): int
    {
        return $this->score;
    }
```

こう

```php:BowlingGame.php
    /**
     * @return int [description]
     */
    public function calculateScore(): int
    {
        $total = 0;
        foreach ($this->Flames as $Flame) {
            $total += $Flame->getScore();
        }

        return $total;
    }
```

テストも通る

```bash
OK (21 tests, 38 assertions)
```

### 終了

まだ改良できる点はあるものの、もう流れはつかめたし
責務も分担できたし一旦クローズ。

気が向いたら気合の入ったリファウタリングを行ってアップしたい