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

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

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

## Flameクラスの組み込み

### 下記テストがFlameクラスを利用して通るようにする

```php
　　/**
     * 全球ガーター時の1フレーム目の得点
     */
    public function testCalculateFrameScoreAtAllGarter()
    {
        $this->loopRecordShot(20, 0);

        $this->assertEquals(0, $this->BowlingGame->flameScore(1));
    }
```

### アプリケーション側の改修(BowlingGame.php)

```php
　　/**
     * @return int
     */
    public function flameScore(): int
    {
        return 0;
    }
```

↓

```php

    public function __construct()
    {
        .
        .
        .
        $this->Flame = new Flame(); // まずはフレーム一つだけでインスタンス作成
    }

    /**
     * ボーリングの1投球の計算を実行させる
     *
     * @param int    $pin [description]
     * @return void
     */
    public function recordShot(int $pin)
    {
        ++$this->shotNo;

        $this->Flame->recordShot($pin); //投球をフレームに記憶させる
        .
        .
        .
    }


　　/**
     * @param int
     * @return int
     */
    public function flameScore($flameNo): int
    {
        return $this->Flame->getScore($flameNo);
    }
```

```bash
OK (12 tests, 12 assertions)
```

### 責務の分担

__BowlingGame__

- 現在のフレームを知っている
- 投球を適切なFlameに記録する
- Flameの点数を合計する(Flameとは一対多の関係)
- ボーナスの振り分けを行う

__Flame__

- 投球を実際に記録する
- 投球結果を判定する(ストライク、スペアなど)
- ボーナス点を保持している
- 記録した点数を返す

BowlingGameは紐付いているFlameを管理するイメージ

### BowlingGameからFlameが持つべき役割を移植

① フレームの完了を判定する

```
public function 2投するとフレームは終了する() 
```
と書くとエラーが起こるので[ _ ]をつけるようにした

```php
    /**
     * @test
     */
    public function _2投するとフレームは終了する()
    {
        $this->Flame->recordShot(1);
        $this->assertFalse($this->Flame->isFinished()); //2投目が可能
        $this->Flame->recordShot(1);
        $this->assertTrue($this->Flame->isFinished()); //2投したので完了
    }
```

② 実行して怒られる

```bash
1) FlameTest::_2投するとフレームは終了する
Error: Call to undefined method App\Flame::isFinished()

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

ERRORS!
Tests: 13, Assertions: 12, Errors: 1.

```

③ 最小構成で通る処理を書く

```php
class Flame
{
    /** @var  int */
    private $score=0;

    /** @var  int */
    private $shotCount=0;

    /**
     * @param int $pin
     */
    public function recordShot(int $pin)
    {
        $this->score += $pin;
        $this->shotCount += 1;
    }

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

    /**
     * @return boolean
     */
    public function isFinished()
    {
        if ($this->shotCount > 1) {
            return true;
        }
        
        return false;
    }

}
```

④実行する

```bash
OK (13 tests, 14 assertions)
```

⑤ ストライクへの対応

__テスト__

```php
    /**
     * @test
     */
    public function _10ピン倒した時点でフレームは終了する()
    {
        $this->Flame->recordShot(10);
        $this->assertTrue($this->Flame->isFinished()); //2投したので完了
    }
```

__テスト実行__

```bash
1) FlameTest::_10ピン倒した時点でフレームは終了する
Failed asserting that false is true.

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

FAILURES!
Tests: 14, Assertions: 15, Failures: 1.

```

__ロジックの修正__

```php
    /**
     * @return boolean
     */
    public function isFinished()
    {
        if ($this->score >= 10 || $this->shotCount > 1) {
            return true;
        }

        return false;
    }
```

__テスト実行__

```bash
OK (14 tests, 15 assertions)
```


⑥ FlameクラスをBowlingGameで使う

__テスト__

```php
//BowlingGame

    /**
     * @test
     */
    public function _全ての投球が1ピンだと全フレーム2点()
    {
        $this->loopRecordShot(20, 1);

        for ($i = 0; $i <= 9; $i++) {
            $this->assertEquals(2, $this->BowlingGame->flameScore($i));
        }
    }
```

__テスト実行__

```
There was 1 failure:

1) BowlingGameTest::_全ての投球が1ピンだと全フレーム2点
Failed asserting that 20 matches expected 2.

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

FAILURES!
Tests: 15, Assertions: 16, Failures: 1.
```

__ロジック修正__

```php
//BowlingGame

public function __construct()
    {
        (中略)
        $this->Flames[] = new Flame(); // まずはフレーム一つだけでインスタンス作成
    }


    /**
     * ボーリングの1投球の計算を実行させる
     *
     * @param int    $pin [description]
     * @return void
     */
    public function recordShot(int $pin)
    {
        ++$this->shotNo;

        //配列の一番最後が現在のフレーム
        $this->Flames[count($this->Flames)-1]->recordShot($pin);

        (中略)

        //フレームが完了したら新しいフレームを配列に追加
        if ($this->Flames[count($this->Flames)-1]->isFinished()) {
            $this->Flames[] = new Flame();
        }

        （中略）
    }

    /**
     * @param int
     * @return int
     */
    public function flameScore(int $flameNo): int
    {
        return $this->Flames[$flameNo]->getScore($flameNo);
    }

```

__テスト実行__

```bash
OK (15 tests, 25 assertions)
```


## 感想

再設計したおかげでオブジェクト指向っぽくなった
久しぶりに記事の続きやってるけど、テストあるから
正しく動いてるかわかるのがいいｗ
そろそろおしまいかな。。