テスト駆動開発をやってみよう

## これはなに？

- ROXX社の社内勉強会にて開催した、テスト駆動開発のワークショップのためのスライドです
- 割と筆者の主観が入りまくっています
- 某ライオン氏のワークショップに比べれば品質は悪いと思いますので、使う場合はその辺ご了承ください

---

## お品書き

- そもそもテストって何？
- テスト駆動開発とは何か
- テスト駆動開発をやってみよう

---

# そもそもテストって何？

---

## ソフトウェアテスト

- 作成したソフトウェアの正しさを、実際の動作とその出力を通して確認すること
- 手動テストと自動テストがある
- エンジニアの文脈では自動テストで話す場合が多い

---

## 自動テスト

- 何らかの実装ないし仕様書をもとに、機械が実行するテスト。
- 機械が実行するので、テスト中も人間は別の作業をすることができる
- 機械が実行するので、同じテストを（ほぼ）同じように実行できる

---

## なぜ自動テストをするのか

- https://qiita.com/niisan-tokyo/items/ff793e271fd35b1a20bb
- 常にテストを実行することで、既存の実装が破壊されていないことを確認できる
- 既存の実装のすべてが破壊されていないかどうかを、新しい実装ごとに手動確認するのは現実的に不可能であり、自動テストを使うしかない
- つまり、運用しつつ開発を続けるソフトウェアにおいて、自動テストは不可欠

---

改築したら別の個所がぶっ壊れた。
テストで防ぎたいのはこういうやつ。
![kaichiku.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/0c7d0680-903e-2ec2-484c-df7a149af789.png)


---

## 自動テストは難しいのか

- https://qiita.com/niisan-tokyo/items/cf445985f951291a1b34
- テストコードは`print`や`echo`が書ければ誰でもできる
    - つまり、むしろ簡単だ！
- むしろ、新人さんにはまずテストから教えるべき
    - https://speakerdeck.com/niisantokyo/xin-ren-sandemotesutowoshu-kubekidatuteiuhua
- テストコードを作るのは誰だってできるけど、テストする環境を用意するのは別の話
    - ルールに従うのではなく、ルールを作る側と言えばわかりやすいかも？

---

## テストの手順
### 基本的にどのテストも、以下のように書かれる

```
public function test_something()
{
    Config::set('number', 1);// 前提条件の設定(ない場合もある)
    
    $object = new Something;
    $result = $object->run();// テスト対象を実行する

    $this->assertEquals(1, $result);// 結果の検証
}
```

流れが決まっているため、テストを書くときは基本的に悩まずに書くことができる

---

## 実装とテストの関係性

- テストが通っている限り、実装を変更することができる
    - いわゆるリファクタリング
- 実装が変わっていなければ、テストの修正・追加をすることができる
    - テストコードのメンテ(個人的にあまり必要ないとは思う)
- 片方を固定することで、もう片方の変更を可能にする
    - 双対性というらしい

---

# テスト駆動開発

---

## テスト駆動開発とは？

- テストを、仕様の確認やリグレッションのためのツールとしてだけではなく、開発の促進にも利用してしまおうという開発手法
- TDD(Test Driven Development)などと略される
- きれいなコードに仕上げたいという目的がある

---

## テスト駆動開発の手順

0. TODOリストを作成する
0. TODOリストから一つ選んで、テストを作成し、失敗することを確認する(RED)
0. テストが通るように最小限の実装をして、テストが通るようにする(GREEN)
0. テストが通る状態を維持したまま、コードを整理し、きれいな状態にする(REFACTORING)
0. 2~4を繰り返し、TODOリストがなくなったらタスクを完了とする

---

## リファクタリング

- 実装を、その振る舞いを変えずにコードを整理して、わかりやすく拡張性がいい感じに改善すること
- 現時点での実装に対し、自分の考えうる最良の実装にしてやるといい
- ここが設計の頑張りどころ。つまり、設計は後からやってくる
- 新規の実装だけでなく、「修正」のタスクでももちろんやるべき。つまり、昔のコードに手を入れる時でも遠慮なくやる

---

## テスト駆動開発の利点

- タスクのゴールが明確化する
    - TODOリストを作ったのだから、それを全部こなせば、それすなわちタスクの完了である
- 実装開始が早い
    - 経験的に、具体的な実装が存在している方が、課題にしろ困難にしろ見つけやすい。どんなに汚くても、早めにモノを用意しよう
- わからない箇所、難しい箇所を特定できる
    - TODOリストをこなすなかで、止まった地点が難しいところである。知恵を絞り、仲間と協力して難所をくぐり抜けよう
- リファクタリングにより最終的にきれいなコードを提供できる
    - 動くコードはいわゆるたたき台である。これを元にして良いものに仕上げよう
- 過去のコードでも修正タスクでリファクタリングすることで、常にコードの改善を進めていける
    - 昔の恥ずかしいコードを見つけちゃっても、さっさと直そう。テストがあり、動作が保証されているのだからこそ、それが許される

---

## テスト駆動開発とペアプロの相乗効果

- TODOリストをお互いに確認し合える
    - 自分一人ではなく、ほかのメンバーと一緒決めるので、より信頼できるゴールだといえる
- 難しい部分の実装が出てきた時に、相談し合える
    - ヘルプを求めたいと感じた時、すぐそこに相棒がいるのだから、相談も捗るというもの
- リファクタリングでお互いの方針をぶつけ合い、よりいい感じの設計へと導ける
    - お互いがいいと思うモノを合わせることで、より良い実装へと昇華させよう

---

# FizzBuzzによるテスト駆動開発の例

---
## TODOリストを作る
PHPのテストの書き方はわかっているので、TODOリストをメソッド形式にして書き出す。

```FizzBuzzTest.php
<?php
namespace Tests;

use App\FizzBuzz;
use PHPUnit\Framework\TestCase;

class FizzBuzzTest extends TestCase
{

    private FizzBuzz $obj;

    public function setUp(): void
    {
        $this->obj = new FizzBuzz;
    }

    /**
     * @test
     */
    public function 値に1をいれたら1を返す()
    {

    }

    /**
     * @test
     */
    public function 値に2を入れたら2を返す()
    {

    }

    /**
     * @test
     */
    public function 値に3をいれたらFizzを返す()
    {

    }

    /**
     * @test
     */
    public function 値に6をいれたらFizzを返す()
    {

    }
```
---

## 1を入れると1を返すテストを書いて失敗する
ちょっと見ずらいけど、`$this->assertEquals('1', $this->obj->run(1));`って書いてある
![fizzbuzz1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/f5cc0622-bc39-1737-e5f1-2a835dba58f2.png)
何も実装していないので、動くわけがない。

---

## 動くコードにする
動くコードを作る
![fizzbuzz2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/2dc8a99a-530c-bc6e-b12d-7c708567d81f.png)
最小限のもっとも単純なコード。
これで正しいのだ。

---

## 2を入れたら2を返してくれるか？

![fizzbuzz3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/ddd83ae0-76e1-e8b4-6189-3ef8a8d6bdb4.png)
失敗しているので、開発が必要だ

---

## 動くコードにする
自分自身を返すのが手っ取り早いので、それでやる。

![fizzbuzz4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/44ecb6c8-9e17-9506-d34c-8e713912d167.png)
例題が簡単すぎるので、リファクタリングをする必要に駆られない。。。

---

## 3を入れるとFizzが返る
まあ、動きません。

![fizzbuzz5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/dd7d9ae9-144f-9082-5dd8-3db4060e3ed4.png)

もちろん、これまでの実装は生きているので、一つ目と二つ目のテストは通っている

---

## テストを通すように実装

![fizzbuzz6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/7341f87a-bc4d-d4dd-b696-cf0fa3a1d156.png)

---

## 繰り返していって
TODOは全部終わった
![fizzbuzz7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/344dbacd-f2d7-c9b1-6403-5d3c6f4d962d.png)
でも、きれいなコードとはいいがたい。

---

## リファクタリング
３行ぐらいに押し込んでみる
テスト通っているから問題ない。

![fizzbuzz8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/987193e1-7991-fff3-f7ee-bae8cf4ce87e.png)

---

# テスト駆動開発をやってみよう

---

## テスト駆動開発をやってみる

- TDDを実践してみて、どういうものかを体験してみよう
- 今回、複数の言語で、最低限テストを書ける環境を用意しているので、参加者の好きな言語での実装が可能である
    - https://github.com/niisan-tokyo/test-templates
- また、今回は基本に忠実にをモットーに、以下のことを実践しよう
    - テストコードにおいて、基本的に１メソッド１アサーション
    - テストファースト
- ピンポンプログラミングを試してみよう
    - 開発者A, Bでペアプロする時
        1. AがTODOの中でテストを作り、そのテストが失敗することを確認する
        1. Bがそのテストが成功するように最小限の実装をする
        1. A,Bで相談し、リファクタリングを実施する
        1. 1~3を繰り返す。繰り返すごとにA,Bの役割を反転させる 
- 全力でリファクタリングしよう

---
## ピンポンプログラミング
### まずTODOリストを作ろう

![pinpon1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/17c7b171-77e3-437e-dd16-08fb668223a9.png)

- 与えられたお題のゴールを見定めよう
- いつまでも考えられてしまうので、タイムボックスを定めるとよい
---
### 片方がまずTODOの中から一つ選んでテストを書く

![pinpon2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/fb48ef51-72c8-3c3b-1f9a-1674b9c0f25e.png)

- もし書きにくそうにしていたら、もう片方がサポートしよう
- スピードを意識してみよう

---
### もう片方が実装する

![pinpon3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/3c44edd7-e522-7f3b-6ff1-469112fc7e06.png)

- 最小の実装を心掛けよう
- こちらもスピードを意識しよう

---
### リファクタリングをする

![pinpon4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/77462567-0ac3-33ed-64f5-3c83b007f85f.png)

- リファクタリングはペアで協力して行う
- 相談をして、「現仕様を変えずに」「コードをきれいに」する
- リファクタリングが必要なければ、「リファクタリングの必要なし」というけつろんをくだしてさきにすすめよ

---

- 簡単そうなテストからどんどん作っていこう
- 最小限の実装するフェーズでは、とにかく速度を優先しよう。こだわりは、リファクタリングフェーズで！