ワイのテスト駆動開発〜偶数ハロー株式会社〜

# 業務中ワイ
ワイ「こないだ見た**[50分でわかるテスト駆動開発](https://channel9.msdn.com/Events/de-code/2017/DO03)**っていう動画おもろかったなぁ」
ワイ「ワイも**テスト駆動開発**やってみたいわ〜」
ワイ「まずは何か簡単な案件で**TDD**を導入してみたいわ〜」

# そんなとき、クライアントから電話
**ジリリリリ〜〜〜〜〜ン！**[^1]

![20C26BBD-EF6B-4AA7-B9B5-BA5454131FB8.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295452/7e735946-75d7-6c71-2246-d9f288a9d360.jpeg)

ワイ「もしもし〜？」

？？「もしもし、**株式会社ブラック**の**暗井 暗人（くらい・あんと）**ですー」

ワイ「ああ、暗井はんでっか」
ワイ「いつもお世話になってます〜」

暗井「この前ご相談した件なんですけど、正式に発注お願いしますわー」
暗井「株式会社**偶数ハロー**さんのWEBサイトの件ですわー」

ワイ「どんな案件でしたっけ？」

暗井「**偶数ハロー**いう社名にちなんで、」
暗井「トップページにちょっとした仕掛けを設置しよう、いうやつですわ」
暗井「要は───」


> - ユーザーが奇数を入力したら、その数値をそのまま表示する
> - ユーザーが偶数を入力したら、「ハロー」と表示する

暗井「───っていうフォームをトップページに設置するんですわ」

社長「（何やその**クソみたいな**フォーム・・・）」

暗井「フォームの見た目とかはもう出来てるんですけど」
暗井「**数値かハローを返す関数**、これが作れませんで」
暗井「そこの関数だけ作って欲しいんですわ」
暗井「予算は**800万**ですわ」

ワイ「かしこまりやで」
ワイ「早速そのプログラム作ってみますわ」

# ちょうどいい案件が舞い込んできた
ワイ「いや〜」
ワイ「ちょうどTDDの練習に**おあつらえ向き**な」
ワイ「**シンプルな案件**が舞い込んでくるもんやな〜」

社長「（**筆者がそのようにストーリーを作ってるからな・・・**）」
社長「（なんや**株式会社偶数ハロー**て。**何を願って命名したんや**）」

ワイ「ほな**TDD**していこか〜」

# テストツールを導入する
ワイ「ほな**[Jest](https://jestjs.io/docs/ja/getting-started)**いう、有名なテストツールでも導入してみよか」
ワイ「`yarn add --dev jest`と」
ワイ「おお、簡単やな」
ワイ「そんで、`evenHello.js`いうファイルでも作っとこか」

```javascript:evenHello.js
function evenHello() {
    
}
module.exports = evenHello;
```
ワイ「こうやな」
ワイ「**`evenHello`**いう関数を作って、エクスポートするわけや」
ワイ「次はテストを書くファイルを作るで」

```javascript:test.js
const evenHello = require('./evenHello');

// ここにテストを書いていく
```
ワイ「こうやな」
ワイ「さっきの`evenHello.js`を読み込んで」
ワイ「それに関するテストをここに書いていくんや」

ワイ「ほな、まず関数を書いていこか」
ワイ「とりあえず───」

> - 奇数を渡したら、その数値をそのまま返す

ワイ「───って部分を実装してみよか」

```javascript:evenHello.js
function evenHello(num) {
    return num;
}
module.exports = evenHello;
```

ワイ「こうやな」
ワイ「あれ・・・？」

# これではいつも通りやん

ワイ「アカンアカン」
ワイ「無意識に**実装から始めてもうた**わ」
ワイ「ちゃうねん」
ワイ「**テスト駆動開発**なんやから、**テストが先**や」
ワイ「実装するより前に、要件を満たした**テストをまず書く**」
ワイ「そして、そのテストはもちろん通らへん」
ワイ「実装してないからな」
ワイ「そんで、そのあと、テストが通るように実装をしていくねん」

ワイ「先に**要件をテスト化**してから実装するから」
ワイ「**正解に向かってしか進めへん**って感じやな」
ワイ「そのためには、ちゃんと要件定義をせなあかんけどな」

ワイ「せやから・・・」
ワイ「**一旦`evenHello.js`は削除や！**」

# 仕切り直してテスト駆動開発

ワイ「まずは**要件定義**をしてみよか」
ワイ「今回作るべき関数の要件は」

> **要件チェックリスト：**

> - [ ]  奇数を渡したら、その数値をそのまま返す
- [ ]  偶数を渡したら、「ハロー」を返す

ワイ「こうやな」
ワイ「さらに細かく、具体的に定義していくで」

> **要件チェックリスト：**

> - [ ]  奇数を渡したら、その数値をそのまま返す
    - [ ]  `1`を渡したら、`1`を返す
    - [ ]  `3`を渡したら、`3`を返す
- [ ]  偶数を渡したら、「ハロー」を返す
    - [ ]  `2`を渡したら、「ハロー」を返す
    - [ ]  `4`を渡したら、「ハロー」を返す

ワイ「こんな感じやな」
ワイ「ほな、まずは」

> - [ ]  `1`を渡したら、`1`を返す

ワイ「これのテストを書くで」

```javascript:test.js
const evenHello = require('./evenHello');

test('1を渡したら、1を返す', () => {
    expect(evenHello(1)).toBe(1);
});
```

ワイ「こうやな」
ワイ「ほんでテスト実行や」
ワイ「`yarn test`と」

> Test suite failed to run
> Cannot find module './evenHello' from 'test.js'

ワイ「せやな」
ワイ「`evenHello`が`Cannot find`やから`failed`や！」
ワイ「`evenHello.js`はさっき削除したからな」
ワイ「ほな、テストを通すために、もう一回`evenHello.js`を作るで！」

```javascript:evenHello.js
function evenHello(num) {
    
}
module.exports = evenHello;
```

ワイ「こうやな」
ワイ「テストに合わせて、`evenHello.js`っていうファイルを作って」
ワイ「テストに合わせて、`evenHello`いう関数を書く、と」
ワイ「**全てはテスト駆動**や！」
ワイ「ほんで、テスト実行、と」

> **テスト結果：**
> <font color="red">✕</font> 1を渡したらそのまま1を返す

ワイ「お、一応テストは走ったけど、**バツ印**やな」
ワイ「ちゃんと**実装してへん**もんな」

ワイ「ほな、まずこのテストだけ通るように仮実装してみよか」

```javascript:evenHello.js
function evenHello(num) {
    return num;
}
module.exports = evenHello;
```

ワイ「こうやな」
ワイ「引数`num`を**そのまま返す**わけや」
ワイ「ほんで、テスト実行、と」

> **テスト結果：**
> <font color="green">✓</font> 1を渡したら、1を返す

ワイ「お、今度は**緑のチェック**がついたな」
ワイ「ここのテストは通ったいうわけや」

> **要件チェックリスト：**

> - [ ]  奇数を渡したら、その数値をそのまま返す
    - [x]  `1`を渡したら、`1`を返す
    - [ ]  `3`を渡したら、`3`を返す
- [ ]  偶数を渡したら、「ハロー」を返す
    - [ ]  `2`を渡したら、「ハロー」を返す
    - [ ]  `4`を渡したら、「ハロー」を返す

ワイ「進捗としては**↑こんな感じ**やな」
ワイ「ほな次は───」

> - `3`を渡したら、`3`を返す

ワイ「───これのテストを書いてみよか」

```test.js
const evenHello = require('./evenHello');

test('1を渡したら、1を返す', () => {
    expect(evenHello(1)).toBe(1);
});

test('3を渡したら、3を返す', () => {
    expect(evenHello(3)).toBe(3);
});
```

ワイ「これは、`evenHello`関数は特にいじらなくてもテスト通りそうやな」
ワイ「ほな**テスト実行**、と」

> **テスト結果：**
> <font color="green">✓</font> 1を渡したら、1を返す
  <font color="green">✓</font> 3を渡したら、3を返す

ワイ「よっしゃ、通ったな」
ワイ「ほな次は・・・テストをまた書くんやな」
ワイ「**テストが先**やからな」

```javascript:test.js
test('2を渡したら、「ハロー」を返す', () => {
    expect(evenHello(2)).toBe('ハロー');
});
```

ワイ「↑このテストを追加や」
ワイ「ほんで、テスト実行、と」

> **テスト結果：**
> <font color="green">✓</font> 1を渡したら、1を返す
  <font color="green">✓</font> 3を渡したら、3を返す
  <font color="red">✕</font> 2を渡したら、「ハロー」を返す

ワイ「せやな」
ワイ「**偶数ハロー機能**はまだ実装してへんからな」
ワイ「よっしゃ、今度は実装や」

```javascript:evenHello.js
function evenHello(num) {
    if (num % 2) {
        return num;
    }
    return 'ハロー';
}
module.exports = evenHello;
```

ワイ「引数`num`を`2`で割って」
ワイ「余りがあったなら、`num`は**奇数**やって事やから」
ワイ「そのまま`num`を返すんや」
ワイ「逆に、余りがないなら**偶数**やから」
ワイ「返すのは`ハロー`や」

ワイ「これでええやろ」
ワイ「テスト実行、と」


> **テスト結果：**
> <font color="green">✓</font> 1を渡したら、1を返す
  <font color="green">✓</font> 3を渡したら、3を返す
  <font color="green">✓</font> 2を渡したら、「ハロー」を返す

ワイ「うんうん、せやろな」

ワイ「なんかこう、これまでの全要件をテストしてくれるから」
ワイ「継ぎ足し継ぎ足しでコードを書いていっても」
> ワイ「**さっきまではちゃんと出来てた部分が、知らんうちにダメになってたやん！**」

ワイ「ってことがないから安心やな」

ワイ「ほな最後は」

> - [ ]  `4`を渡したら、「ハロー」を返す

ワイ「↑この要件のテストでも書いとこか」

```javascript:test.js
test('4を渡したら、「ハロー」を返す', () => {
    expect(evenHello(4)).toBe('ハロー');
});
```

ワイ「こうやな」
ワイ「ほんで、テスト実行、と」

> **テスト結果：**
> <font color="green">✓</font> 1を渡したら、1を返す
  <font color="green">✓</font> 3を渡したら、3を返す
  <font color="green">✓</font> 2を渡したら、「ハロー」を返す
  <font color="green">✓</font> 4を渡したら、「ハロー」を返す

ワイ「よっしゃ、偶数ハロー関数の完成やな」
ワイ「チェックリストでいうと───」

> **要件チェックリスト：**

> - [x]  奇数を渡したら、その数値をそのまま返す
    - [x]  `1`を渡したら、`1`を返す
    - [x]  `3`を渡したら、`3`を返す
- [x]  偶数を渡したら、「ハロー」を返す
    - [x]  `2`を渡したら、「ハロー」を返す
    - [x]  `4`を渡したら、「ハロー」を返す


ワイ「───こんな感じや」
ワイ「**オールOK**や」

# せっかくだから、チェックリストに近いテストを
ワイ「確かJestの機能で」
ワイ「**テストをグループ化**できんねん」

```javascript:test.js
describe('奇数を渡したら、その数値をそのまま返す', () => {
    test('1を渡したら、1を返す', () => {
        expect(evenHello(1)).toBe(1);
    });
    test('3を渡したら、3を返す', () => {
        expect(evenHello(3)).toBe(3);
    });
});

describe('偶数を渡したら、「ハロー」を返す', () => {
    test('2を渡したら、「ハロー」を返す', () => {
        expect(evenHello(2)).toBe('ハロー');
    });
    test('4を渡したら、「ハロー」を返す', () => {
        expect(evenHello(4)).toBe('ハロー');
    });
});
```

ワイ「こないな感じや」
ワイ「チェックリスト同様に**入れ子にしてやるんや**」
ワイ「そしたら、今後この案件に関わる子らにとっても分かりやすいやろ」
ワイ「ほんで、テスト実行してみよか」

> **テスト結果：**
> 奇数を渡したら、その数値をそのまま返す
  　<font color="green">✓</font> 1を渡したら、1を返す
  　<font color="green">✓</font> 3を渡したら、3を返す
  偶数を渡したら、「ハロー」を返す
  　<font color="green">✓</font> 2を渡したら、「ハロー」を返す
  　<font color="green">✓</font> 4を渡したら、「ハロー」を返す

ワイ「うんうん」
ワイ「ちゃんと**テスト結果もグループ分け**されてて直感的やん」

ワイ「はぁ〜」
ワイ「**デグレ**[^2]してないことが**テストによって保証されてる**から」
ワイ「安心して進んでいける感じがしたな」

ワイ「ほな、クライアントはんに連絡してみよか」

# 暗井暗人はんに報告
ワイ「もしもし〜、暗井はん？さっきの関数作りましたで〜」
ワイ「いつものリポジトリに上げといたんで、確認してくださいやで〜」

暗井「はいよー」
暗井「うんうん、動かしてみたけどええ感じやね」

ワイ「よかったですわ〜」

暗井「・・・**あっ！！！**」

ワイ「な、なんでっか・・・」

暗井「株式会社**偶数ハロー**さん、先週**社名変更**したんでしたわ」
暗井「その名も、株式会社**偶数ハロー・マイナスワールド**」
暗井「それに伴って関数の仕様にも───」

> - 負の値を渡したら、「ワールド」を返す

暗井「───これが追加になるんでしたわ」
暗井「さっき言い忘れてましたわ」

ワイ「はぁ・・・・」

暗井「すんまへんけど、その修正お願いしますわ・・・」
暗井「ちなみにこの修正も、さっきの**800万**の中で収まりまっか・・・？」

ワイ「何とか**800万**で収めますわ・・・」

# 修正開始
ワイ「はぁ、一番やる気なくなるやつや・・・」
ワイ「でもまぁ、テスト駆動開発の練習にはちょうどええかもな」
ワイ「さっそく、要件定義＆テストから始めるで！」
ワイ「追加の要件は・・・」

> - [ ]  負の値を渡したら、偶数奇数に関わらず「ワールド」を返す
    - [ ]  `-1`を渡したら、「ワールド」を返す
    - [ ]  `-2`を渡したら、「ワールド」を返す

ワイ「こんな感じやな」
ワイ「せやから・・・」

```javascript:test.js
describe('負の値を渡したら、偶数奇数に関わらず「ワールド」を返す', () => {
    test('-1を渡したら、「ワールド」を返す', () => {
        expect(evenHello(-1)).toBe('ワールド');
    });
    test('-2を渡したら、「ワールド」を返す', () => {
        expect(evenHello(-2)).toBe('ワールド');
    });
});
```

ワイ「↑これをテストに追加や！」
ワイ「そんでテスト実行！」

> **テスト結果：**
負の値を渡したら、偶数奇数に関わらず「ワールド」を返す
　<font color="red">✕</font> -1を渡したら、「ワールド」を返す
　<font color="red">✕</font> -2を渡したら、「ワールド」を返す
奇数を渡したら、その数値をそのまま返す
　<font color="green">✓</font> 1を渡したら、1を返す
　<font color="green">✓</font> 3を渡したら、3を返す
偶数を渡したら、「ハロー」を返す
　<font color="green">✓</font> 2を渡したら、「ハロー」を返す
　<font color="green">✓</font> 4を渡したら、「ハロー」を返す


ワイ「せやな」
ワイ「追加したテストだけ**バツ**。想定通りや」
ワイ「ほんで今度は実装や！」

```javascript:evenHello.js
function evenHello(num) {
    if (num < 0) {
        return 'ワールド';
    }
    if (num % 2) {
        return num;
    }
    return 'ハロー';
}
module.exports = evenHello;
```

ワイ「こうやな！」
ワイ「ほんでテスト実行や！」

> **テスト結果：**
負の値を渡したら、偶数奇数に関わらず「ワールド」を返す
　<font color="green">✓</font> -1を渡したら、「ワールド」を返す
　<font color="green">✓</font> -2を渡したら、「ワールド」を返す
奇数を渡したら、その数値をそのまま返す
　<font color="green">✓</font> 1を渡したら、1を返す
　<font color="green">✓</font> 3を渡したら、3を返す
偶数を渡したら、「ハロー」を返す
　<font color="green">✓</font> 2を渡したら、「ハロー」を返す
　<font color="green">✓</font> 4を渡したら、「ハロー」を返す

ワイ「うんうん」
ワイ「追加の要件も満たしたし」
ワイ「**元々の機能もおかしくなってへん**」
ワイ「それが可視化されてるから**ものすごい安心感や！**」
ワイ「こんなに安心して追加開発が出来るのは初めてやで・・・」
ワイ「いつも、コード修正するときに」
ワイ「言われた部分は修正できたけど、他んとこ**おかしくなってへんかな**・・・って」
ワイ「心配で夜も**6時間しか**眠れへんかったんや」

社長「（**まあまあ寝てるやん・・・**）」
社長「（あと、そんな**心配なもん**納品すな・・・）」

# まとめ
ワイ「なんか、**追加開発時の安心感がすごい**わ」
ワイ「**デグレ**してへんことを**テストが保証**してくれてんねんもん」
ワイ「あと、テストコード自体が**ドキュメント代わりになる**な」
ワイ「途中から参加したメンバーも、`test.js`を見れば」
ワイ「おー、こんな要件を満たす必要があるんやなー、って分かるやん？」
ワイ「そういう**情報がコードとして残る**のが素晴らしいな」

ワイ「よっしゃ、今度はもうちょい複雑な案件でもテスト駆動開発してみよか！」

**〜おしまい〜**


# おすすめ動画
ワイの記事はだいぶ端折ってる感じやから、興味がある人は↓この動画を見るとええで！

**[50分でわかるテスト駆動開発](https://channel9.msdn.com/Events/de-code/2017/DO03)**
[^1]: 電話の音やで。
[^2]: 前は機能していた部分がバグってしまうことや。