5歳娘「パパ、依存性を注入して？」

## とある休日

ワイ「あ〜、今日は暇やな〜」

娘（**5歳**）「ねえパパ、相談したいことがあるんだけど」

ワイ「おお、娘ちゃん」
ワイ「ええで、何でも相談してや」

娘「実はね、幼稚園で**ある遊び**が流行ってるの」

ワイ「ほうほう、どんな遊びかな？」

娘「ええとね」
娘「サイコロを2回ふって、**2回とも同じ目が出たら勝ち**っていう遊びなの」

ワイ「おお、楽しそうやないかい」

娘「それでね、勝った人には**すご〜くいいこと**があるの」

ワイ「ええ？すご〜くいいこと？」
ワイ「なんや気になるわ、教えてぇや」

娘「**100万円もらえるの**」

ワイ「ととと**賭博**やないかい！！！」

娘「しかも、そのあとね」
娘「なんやかんやで**三食付きの施設**に**ずっと無料**で泊まれるの」

ワイ「いや、それ**ブタ箱**入っとるやないかい！！！」

娘「でもこの間、その遊びが**幼稚園の規則**で禁止されちゃったの・・・」

ワイ「いや**法律**で禁止されてんねん」

娘「それでね」
娘「今後は**リアルマネー**じゃなくて**ポイント**を使って遊ぼうっていう話になったの」

ワイ「ん？ポイント？」
ワイ「どういうこと？」

娘「ええとね、Webアプリを作って」
娘「ブラウザ上でサイコロゲームができるようにして」
娘「お金じゃなくて**ポイントをやり取りして**遊びたいの」

ワイ「おお、それはいい考えやな」
ワイ「それなら合法やからな」

## そこでパパに相談

娘「それでね？」
娘「今そのWebアプリに必要な**関数**を書いてたところなの」
娘「どんな関数かって言うと・・・」

> - サイコロを2回ふる
> - 1回目と2回目の**出目が同じ**なら`"同じだったよ！"`という文字列を返す
> - 出目が同じでなければ`"違ったよ！"`という文字列を返す

娘「↑こんな機能を持つ関数なんだけど、なんかうまく行かないの」

ワイ「ほうほう、書いた関数を見せてくれるか？」

娘「うん」
娘「まずは、1〜6のどれかをランダムに返してくれる`randomDice`っていう関数」

```javascript
// 1〜6のどれかをランダムに返してくれる関数
const randomDice = () => {
  return Math.floor(Math.random() * 6) + 1
}
```

ワイ「なるほど、この関数が**サイコロとしての役割**をするわけやな」
ワイ「ええ感じやん」

娘「もう1つ、目が同じだったかどうかをチェックする関数も作ったの」

```javascript
// サイコロを2回ふって、目が同じだったかどうかをチェックする関数
const sameCheck = () => {
  // サイコロをふる（1回目）
  const roll1 = randomDice()

  // サイコロをふる（2回目）
  const roll2 = randomDice()

  if (roll1 === roll2) {
    return "同じだったよ！"
  } else {
    return "違ったよ！"
  }
}
```

ワイ「なるほどな」
ワイ「さっきの`randomDice`関数を2回実行して」
ワイ「2回の結果が同じだったら`"同じだったよ！"`」
ワイ「同じでなければ`"違ったよ！"`を返す関数って訳やな」

娘「うん」

ワイ「特に問題ないんちゃう？」

娘「そうなんだけど・・・」

## 単体テストがしづらい

娘「この関数にね、今後も機能を追加する可能性があるの」
娘「だから、それに備えて**自動テスト**の用意をしておきたいの」

ワイ「なるほど、機能を追加した際に」
ワイ「逆に今までの機能がおかしくなったりするとマズいから」
ワイ「ちゃんと**全機能を網羅的にチェック**する**自動テスト**を準備しておこうってことやな」

娘「そう」
娘「でもさっきの`sameCheck`関数は、何だかテストがしづらいの」

ワイ「そうなん？」

娘「うん」
娘「`sameCheck`関数を実行するたびに」
娘「`"同じだったよ！"`とか`"違ったよ！"`っていう文字列が」
娘「ランダムに返ってきてることは確認できるんだけど」

> - 1回目と2回目の**出目が同じ**なら`"同じだったよ！"`という文字列を返す
> - 出目が同じでなければ`"違ったよ！"`という文字列を返す

娘「↑この条件を**本当に満たしているのか**、チェックできてない気がするの」

```javascript
console.log(sameCheck())
// -> "違ったよ！"

console.log(sameCheck())
// -> "同じだったよ！"

console.log(sameCheck())
// -> "違ったよ！"

console.log(sameCheck())
// -> "違ったよ！"

console.log(sameCheck())
// -> "違ったよ！"
```

娘「↑ほらね」

ワイ「ああ、確かになぁ」
ワイ「**どの目が出たことで**同じになったのか、もしくはなってないのか」
ワイ「それが分からへんな」

娘「そうなの・・・」

よめ太郎「そんな時は、サイコロを引数として**ぶち込んで**やればええんや」

ワイ「おお、よめ太郎」


## サイコロを引数として渡せばええんや

娘「サイコロを引数として渡す・・・？」
娘「あ、そっか・・・！」
娘「そういうことだよね！」
娘「じゃあ・・・」
娘「**パパ、依存性を注入して？**」

ワイ「**ファッ！？**」
ワイ「パパはちょっと（話題について行けなさ過ぎて）目眩がしてきたから」
ワイ「ここは、ママにやってみせてもらおうや・・・」

よめ太郎「え、ええで・・・」

## 実演開始

よめ太郎「さっきの`sameCheck`関数を」
よめ太郎「ちょっといじって、**引数を受け取れるように**してやるんや」

```javascript
// 引数を1つ受け取れるようにする。
const sameCheck = (dice) => {
  // 引数で受け取ったサイコロをふる（1回目）
  const roll1 = dice()

  // 引数で受け取ったサイコロをふる（2回目）
  const roll2 = dice()

  if (roll1 === roll2) {
    return "同じだったよ！"
  } else {
    return "違ったよ！"
  }
}
```

よめ太郎「↑こんな感じや」

ワイ「ほお、さっきまでは**引数を受け取らない**関数やったけど」
ワイ「**引数を1つ受け取る**ようになったんやね」

よめ太郎「せや」
よめ太郎「実行する時はこんな感じや」

```javascript
const result = sameCheck(randomDice)
```

ワイ「なるほどな？」
ワイ「サイコロの代わりである`randomDice`関数を、引数として渡してやって」
ワイ「それを`sameCheck`関数が受け取って、内部で実行してやるわけか」

よめ太郎「そういうことや」

ワイ「でも、何でそんな回りくどいことすんの？」

## テストの時にはサイコロを差し替えたい

よめ太郎「こうすることで、テストの時に**サイコロを差し替える**ことができるんや」
よめ太郎「**疑似サイコロ**にな」

ワイ「疑似サイコロ・・・」
ワイ「どういうこと？」

よめ太郎「ほな、コード書きながら説明するで」
よめ太郎「まずはな」

```javascript
// 毎回1を返す関数
const mockDice1 = () => {
  return 1
}
```

よめ太郎「↑こんな関数を作るんや」
よめ太郎「これは、毎回`1`が出る**テスト用の疑似サイコロ**みたいなもんや」

ワイ「ほうほう」

よめ太郎「そんで、この**疑似サイコロ**を使って・・・」

```javascript
// 疑似サイコロを使って、sameCheck関数をテストする。
// 必ず1が出る疑似サイコロを渡す。
const message = sameCheck(mockDice1)

// テストしてみる
if (message === "同じだったよ！") {
  console.log("同じだったテスト成功！")
} else {
  console.log("テスト失敗やわ・・・！！！")
}
```

よめ太郎「↑こうや」
よめ太郎「このコードを実行してみるで？」

> 同じだったテスト成功！

ワイ「おお、テスト成功したみたいやな[^1]」

よめ太郎「せや」
よめ太郎「つまり・・・」

> - 1回目と2回目の**出目が同じ**なら`"同じだったよ！"`という文字列を返す

よめ太郎「↑この要件を満たしていることが確認できたやろ？」
よめ太郎「**毎回必ず同じ目が出るサイコロ**やからな！」

ワイ「おお、なるほどな」
ワイ「**出目をコントロールしながら**`sameCheck`関数のテストができるわけやな」

よめ太郎「せや」

> 「このサイコロを使って処理をしてくれ〜い！」

よめ太郎「って感じで、**サイコロを渡して**実行してもらう感じやな」

ワイ「なるほどな」

よめ太郎「ほな次は、目が**同じじゃなかった**場合のテストもしていくで」
よめ太郎「せやから・・・」

```javascript
// 1と2を交互に返す関数
const alternateDice = (() => {
  let num = 2

  return () => {
    num = num === 1 ? 2 : 1
    return num
  }
})()
```

よめ太郎「↑こうやな」

ワイ「なるほど、これは」
ワイ「`1`と`2`が交互に出る、不思議な**疑似サイコロ**ってことか」
ワイ「そんなこともできるんやな」

よめ太郎「せや」
よめ太郎「この疑似サイコロを使って・・・」

```javascript
// 疑似サイコロを使って、sameCheck関数をテストする。
// 1と2が交互に出る疑似サイコロを渡す。
const message = sameCheck(alternateDice)

// テストしてみる
if (message === "違ったよ！") {
  console.log("違った場合のテスト成功！")
} else {
  console.log("テスト失敗やわ・・・！！！")
}
```

よめ太郎「↑こうやな」
よめ太郎「これも実行してみるで？」

> 違った場合のテスト成功！

ワイ「おお、テスト成功したな」

よめ太郎「せや」
よめ太郎「つまり・・・」

> - 出目が同じでなければ`"違ったよ！"`という文字列を返す

よめ太郎「↑この仕様を満たしていることが確認できたわけや」
よめ太郎「1回目と2回目で**必ず違う目が出るサイコロ**やからな」

ワイ「おお、なるほどなぁ・・・」
ワイ「サイコロを引数としてぶち込めるようにしておくことで」
ワイ「単体テストの時には**疑似サイコロに差し替える**ことができるってことか・・・」

よめ太郎「せやな」
よめ太郎「`sameCheck`関数の実行結果は、サイコロの結果に**依存**して変わるから」
よめ太郎「その**依存先**である**サイコロをコントロール**できないと、テストがしづらいわけや」

ワイ「確かに・・・」

よめ太郎「せやから、**依存先**であるサイコロ関数を」
よめ太郎「`sameCheck`関数の中で**そのまま実行したら駄目**なんや」

ワイ「なるほどな〜」
ワイ「**依存先**となるサイコロ君を、**引数としてぶち込めるように**しておくことで」
ワイ「色んなパターンを試しながら自動テストができるわけか・・・」

よめ太郎「そういうことや」
よめ太郎「依存性の注入・・・**DI（Dependency Injection）**ってやつやな」

## まとめ

- 関数から返ってくる値が一定でないと、単体テストがしづらい。  
（乱数生成やAPI通信をする関数など）
- だから、テストの時だけ一部をモック化したいことがある。  
（今回で言えばサイコロ）
- モック化したいものは、関数の中にそのまま書くのではなく、引数として注入できるようにしておく。
- そうすることで、単体テスト時に**モックに差し替える**ことができる。
- すると、色んなパターンを網羅した自動テストが簡単に実現できる。

ワイ「↑ってことやな！」

よめ太郎「せやな！」

## ちなみに

ワイ「**依存性の注入**っていう呼び方より、**依存対象の注入**とかの方がしっくりくるけど」
ワイ「**Dependency Injection**の日本語訳は**依存性の注入**でええのんかいな」

よめ太郎「それなぁ・・・」
よめ太郎「**依存性**を注入してるっていう感じはしないけど、その訳し方で定着してもうたみたいやな」

ワイ「なるほどな」
ワイ「あと、もう一つ気になったんやけど」
ワイ「サイコロ以外にも**何でも注入できそう**やけど、それでエラー起こったりせえへんの？」

よめ太郎「ええところに気づいたな」
よめ太郎「そこは是非とも**TypeScript**を導入して」

> - サイコロとは、以下の条件を満たすものである
>    - 引数なしの関数であること
>    - 数値を返す関数であること

よめ太郎「↑こういった、**型による縛り**を加えた方がええやろな」

ワイ「なるほどな」
ワイ「サイコロの**インターフェース**を定義するんやな」

よめ太郎「そういうこっちゃ」

ワイ「よめ太郎、今日もおおきにやで」
ワイ「そろそろ**15時**やし」
ワイ「ワイも**日本酒**という**依存対象を注入**さしてもらうわ」

よめ太郎「**そこの依存は断ち切らんかい**」

**〜おしまい〜**

## 次の記事もよろしくやで

ゆめみアドベントカレンダー2日目は、goさんの記事やで！
[**AWS認定データアナリティクス専門知識(DAS)を、ビッグデータ専門知識の振り返りとして取得した試験準備やったことまとめ**](https://qiita.com/go_sougen/items/d4bc8f63130cdaeaa8c3)


[^1]: 実際は[Jest](https://jestjs.io/ja/)等の自動テストツールを使用してテストすることが多いです。