What's this?
【良い単体テストの書き方】依存性を排除してナンシーをオトせ!!
というYouTubeに載せた動画の台本。テストから依存性を排除する必要性を語ってみたものの、
かなり早口でまくし立てているので、収録に使った台本をそのまま残しておいた。動画を見た後の復習にでも使ってください。

以下、台本
ハイテンションエンジニアだ!
Good morning Japan, and good evening in the u.s.a!
さて前回はみんなに「なぜテストを書くべきなのか」説明した。
Why should I even care about writing a test?
今回は「良いテストをどうやって書くか」説明したい
How can I write a good test?
そして説明を通じて気づくかもしれないが
良いテストを書こうとすると、結果的にコードそのものがBeautifulになる。
不思議だよな?amazingだよな?
俺の幼馴染のナンシーも、去年すげぇ綺麗なドレスを買ったんだ。
最初は「似合わねーぜ」ってバカにしてたけど、最近じゃナンシーがどんどん綺麗になって、
そのドレスにフィットしてきた。
それと同じさ!
ドレスがナンシーを綺麗にしたように、テストもコードを綺麗にする。
さて今回は、クローラーを題材に考えてみよう。
クローラーを起動すると、ハイテンション.comから画像を根こそぎ引っ張ってきて表示する。
そんなクローラーを作っているとしよう。
さて君はコーディングを始める
まずは、ハイテンション.comにアクセスするコードを書く。
次に、サイトの情報を全部取ってきた後、
画像だけを抽出するコードを書く。
簡単に言うと、情報を取りに行く機能と、情報から画像だけ取り出す機能だ。
It’s a piece of cake, 簡単だよな!
一つの関数に全部コードをまとめて、ささっと作ったとしよう。
そしてデプロイしようと思った直前、
君は前回のハイテンションエンジニアの動画を見てしまう。
俺様の華麗なトークに翻弄されて、
テストを書いてからデプロイした方がいいのかな・・・とためらう。
それは良い傾向だ!
だから君はもう1時間書けて、自分の関数にテストを書くことにした。
ちゃんとサイトからhtmlが取得できることをテストして、
そのデータから画像が取れた事もテストした。
よし、テスト完璧!
今日の開発は終わり、寝よ寝よ。
こんなテストを書いたら、次の朝、たぶん君のテストは死んでいる。
全滅だ。お前のテストはすでに死んでいる。you’re dead to me. Estas muerto, du bist tod.
どうしてテストが死んでるのかって?簡単さ、テストに依存関係が多すぎる。
君のコードをおさらいしよう。
サイトにアクセスする。
そして画像だけを取り出す。
君は昨日の夜にテストを実行して、サイトにアクセスして、
そこから画像だけを取り出せる事をテストした。
でも、それじゃダメなんだ。
昨日の夜はサイトに繋がった。
でも今日はどうだい?パソコンがネットに繋がっていないかもしれない。
ハイテンション.comがドメインを変えて、ローテンション.comになってるかもしれない。
サイトに繋がらなかったら、何も取ってこれないだろ?
画像を取り出そうにも、何もないところから取り出そうとするから、テストが落ちる。
つまり君のテストは、ハイテンション.comの状態に依存している。
俺がママのミートパイに依存しているように、君のテストは、何かに依存している。
それじゃ良いテストを書いてる事にならないんだ!
良いテストってのは、何回動かしても、どこで動かしても、絶対動く。
Jenkinsでもcircleciでもシベリアでも、ベガスのカジノだろうが、
どこでも動くテストを書かなきゃいけない。
そのためにはこれから、君のテストから依存性を取り除かなきゃいけない。
どうするかって?
ここで登場するのが、テストダブルっていうありがたーい奴らだ。
ダブルちゃんって読んでおこう。いいかい、これがダブルちゃんだ。こっちは俺の指だ。
ダブルちゃんの語源はスタントダブルって言葉からきてる。
スタントマンとかいるだろ?あいつらの正式名称はスタントダブルって言ってな、
ダブルってのはつまり代役だ。
スタントダブルが活躍している間、俳優は何もしない。
ボケーっとはなくそほじって待ってる。
テストのダブルも一緒だ。
今回でいうと、本来リクエストを飛ばすところを
ダブルちゃんにお願いすることで
リクエストは何もしないで鼻くそほじって待つようになる。
ちょっと難しいけど、説明するぞ。
君の関数の中に、リクエストを飛ばしている部分が実装されているはずだ。
ここがリクエストだとしよう(指で示す)
そこをダブルちゃんに置き換えるんだ。ぴーん!(指で弾いて、ダブルが置き換える)
そしてダブルちゃんと約束を交わす。
「もし君が呼ばれたら、HTTPリクエストは飛ばすな。
ただし、俺様がサンプルhtmlを用意しておいたから、それを返してくれ」ってな。
良いかい。もう一回いうぞ。ダブルちゃんと約束をするんだ。
「君が呼ばれたら、絶対にリクエストは飛ばすな。代わりに俺様が用意したサンプルを返せ」
ってな。
すると何が起きるか?
テストを実行した時、リクエストは飛ばない。
ダブルちゃんは約束どおり、リクエストを飛ばさずに、俺が用意したサンプルhtmlをしてくれる。
あとはサンプルhtmlから画像が取り出せることを確認したら、テストが通る。
テストの質をあげたければ、もっと色んな種類のサンプルhtmlを用意して、
全種類で画像の取り出しが成功することを確認すれば良い。
わかるかい?俺はダブルちゃんを使ったテストでは、リクエストを飛ばしていない。
リクエストを実際に飛ばさないってことは、ネットに繋がらなくてもテストは通るだろ?
そうすれば、どこで何度テストしても、テストが通る。
ネットに繋がっていなくても、君がチーズケーキを食っていようと、ママのミートパイだろうと、テストが通る。
ハイテンション.comがローテンション.comになっていうようが、テストが通る。
これが依存性を除いたテストだ。
君のテストは通信と外部サイトの依存から解放された。
鋭い視聴者はこう考えるかもしれない。
「おいおいハイテンション、それってテストした事になるのかい?
だって君はサンプルデータから画像を取り出せることを確認しただけじゃないか」
いい質問だ。君はいつかスタンフォードにいけるだろう。そして答えはこうだ。
「君はハイテンション.comからリソースを取得できることはテストしていない」
「ただし、取ってきたリソースから画像だけを取り出せることはテストしている」
わかるかな。
今までは全部テストしようとしてた。
外部に通信する、そして画像を取り出す。
それを分離して、取り出す部分だけのテストを書いたんだ。
そうすることで、テストは依存性が減って、安定したテストになった。
こうやってテストの依存性を減らしていくほどに、
君の単体テストは安定して、確かなものになっていく。
例えば取り出した画像をディスクに書き込む機能を追加した時だってそうさ。
そのまま機能を関数に追加して、全部を試すテストを書いたら
君のテストはmacでは動くけど、windowsやlinuxでは動かないかもしれない。
つまりテストがディスクに依存してる。
わかるかい?ハイテンション.comにリクエストを飛ばした時と同じさ。
ディスクの種類によってテスト結果が変わっちゃうんじゃ、
安定したテストとは言えない。
そんな時はダブルちゃんを使って、ディスクの代わりを勤めてもらうことで
また依存性をテストから取り除ける。
つまり良い単体テストとは、依存性の低い、安定したテストだ。
テストの対象をどんどん絞っていくんだ。
リクエストとか、ディスクとか、そういう依存を減らして
君が書いたコードに対するテストを書くんだ。
それが単体テストだ。
そしていい単体テストを描こうとするうちに、君は気付くだろう。
依存性を排除しようとすると、コードの構造を色々と変えなくっちゃいけない。
依存性を外部から注入するDependency Injectionとか、
責務を分担するLayered Architectureとか、
たくさん概念を覚えて、色んな設計を操って行かなくちゃいけない。
最初はただ面倒くさいし、コードを書く時間が増えるだろう。
でも間違いない。いいテストを書くことは、絶対に、今後の君のためになる。
ちょっとでも僕を疑い始めた君は、前回の動画「テストを制するものは開発を制する」をみて
もう一度僕にケツをspankingされた方がいい。
テストを書くのは素晴らしいことだからだ。
さて、とてつもなく鋭い視聴者は気づいたかもしれない。
おいおい、ハイテンションは途中からテストのことを「単体テスト」って呼び始めたぜ?
こいつ表記揺れしてるぞ最低だ、炎上させようってな。
そう、今回の話は実は「単体テスト」の話。
テストの中でも、ごく一部のテストの話だ。
世の中にはe2eテストとかUIテストとか、色んなテストがある。
中には依存性を減らすのが難しかったり、減らす意味のないテストもある。
何でもかんでも依存性を減らせばいいってわけじゃない。
でも、まずは今日話した単体テストから考えてみるといいぞ。
コードの書き方がガラリと変わると思う。
そしてテストに興味を持った君は、他のテストも調べてみるといいぞ。
Auf wiedersehen.