古めの組織に導入する自動テスト・アンチパターン集

# アジェンダ
- そもそも、なぜ自動テスト？
- 自動テストと騒ぐと冷たい目で見られる事もあるという話
- 個人・組織としての失敗談と、その処方箋

# 書かないこと
テストがないレガシーコードにテストを組み込む方法については、触れない

# そもそも、なぜ自動テスト？
自動テストは、うまく導入すれば、プロダクト品質の担保とユーザへのデリバリー速度に大きく寄与することが分かっているため。

## Web業界の先端が、自動テストの効能を体現してる
たとえばこんなフロー

 1. テストコードを書く
 2. プロダクトコードを書く
 3. テストコード、プロダクトコードをコミットしてプルリクを出す
 4. プルリクをマージすると、CI環境がロジックのフルテスト、UIのフルテストを走らせる
 5. テストが全部通ったらそのまま自動的にデプロイする

### コンピュータに任せられることは全てコンピュータに

テスト実施は単純作業なので、人間が実施しなくても良いようになっている

- 人間が手で確認するより明らかに速い
- 人間と違って何度実行してもコンピュータは飽きない

### どうしても人間がやらなきゃいけないこと
プロダクトがどう動くのが正しいのかは人間が考えなければいけない

- テストの設計は、人間がやらなければいけない
 - 従来は人間向けのドキュメントでこれを表現してきた（テスト仕様書や設計書）
 - これからは正しさをコンピュータに向けてテストコードで表現する

### 人間が実施する限り、ミスから解放されない
どんなにダブルチェックとか言っても人間はミスをする生き物

- たいがいの組織では、ミスをするとチェック工程が増える
- チェック工程が増えると、一時的にうまくいく
- ただ、増えたチェック工程を2回・3回と人間がやっていくうちに、人間はやっぱりミスをする
 - で、またチェック工程が増える(悪循環)

テストを自動化することによって、こういった悪循環に陥るのを避けたい

## いつか目指したいゴール地点はココ
プロダクトコードとテストコードをマージすると、CI環境がすべてのUI/ビジネスロジックを検証してくれる状態

- Webではできてたりする
- 人間が手で実施するテストを無くせば、もっと重要な作業に専念できるようになる
- CI環境によって、プロダクトの健康状態がほぼ全自動で保たれ続ける
- テストをコンピュータに任せるのがうまくなると、テスターを雇うコストをプログラマが吸収できる

コスト面も安くなって、プロダクトの健康が保てて、エンジニアの健康も保てる

# ただ大きめの組織で自動テストというと苦々しい顔をされることも多い
よくあるのは

- ユニットテストって膨大に金かかるじゃん
- ちょくちょくその話は出るけど、継続してこう！ってほどうまくいかないんだよねぇ
- 継続が約束されてるプロジェクトならともかく、一回きりの開発では絶対ペイしないからやめろ

耳にタコができるほど言われたし、おそらく、これらは結構正しい。

## なぜこういったことが起こるのか？
自分なりの結論:
自動テストを組織に導入するというのは、とても失敗しやすいから

## と て も 、失 敗 し や す い か ら
大事なことなので二回言いました。

2013年にt-wadaさんのTDDBCを聞いてから、自分もたくさん失敗した。
（たぶん先輩方も、いろいろ失敗してる）
この話は、その総括です。

## 失敗って？
ここでは、下記2つを失敗と定義する。

- コストパフォーマンスが悪く、導入したのに長期的にROI(費用対効果)が悪化する
- ROIが悪い作業(ダメな自動テスト)を強制されたことが原因で、組織や個人が自動テストにアレルギーを持ってしまう

# 個人としての失敗と、組織としての失敗と
では本題。ざっと、こんな失敗をしてきました。

- 個人としての失敗パターン
 - テストコードをコピペだらけにしてしまう
 -  テストコードを自動で実行する環境がない
 -  プロダクトコードのテスタビリティが低い
 -  自動テストが完了するまでの時間が長すぎる
 -  UIの自動テストを必死に書く(スマホ業界限定？)
- 組織としての失敗パターン
 - 『まずはC0カバレッジ100%』という定量的な品質基準を立てる
 -  仕様レベルではなく、プログラムレベルのテスト自動化を真っ先に始める
 -  (失敗?)『継続が確実ではない案件で自動テストを作ってもコスパが悪いため、導入しない』という判断をする

## 個人としての失敗
### **失敗 : テストコードをコピペだらけにしてしまう**
**起きる問題**
仕様変更での影響範囲が甚大になる

- プロダクトコード品質守れているが、テストコードの品質は低いという状態
- コードが腐ると手に負えなくなるのは、テストコードもプロダクトコードも同じ
- テストコードはプロダクトコードと一緒に保守していくもの

**処方箋**
最低限のDRY原則(Don't Repeat Yourself)

 - 最低限似たような重複コードは1つにまとめる
 - できれば、プロダクトコードと同レベルのクラス設計をして、プロダクトコードと同レベルの品質を保つ

パラメタライズドテストができるテスティングフレームワークを使う

- DRYの先にあるもの。1つにまとめたテストコードに、入力値と期待値のパターンを網羅的に入れて確認する方法
- JavaならSpockとか（一応、JUnitでもパラメタライズドテストはできるが、出来が微妙らしい)
 - http://www.slideshare.net/bikisuke/spocks-world
 - http://qiita.com/euno7/items/1e834d3d58da3e659f92
- C#であれば、最近のNUnitはよくできてる

### **失敗 : テストコードを自動で実行する環境がない**
**起きる問題**
長期で保守している間に、気づいたらテストコードがいつのまにか動かなくなっていることがある

- 『テストコードの実行にはそれなりに時間がかかるし、なにかの区切りごとに実行しよう』
- →本人の意識の問題になるため、継続が困難になる。

**処方箋**

- CI環境でバージョン管理システムをポーリングし、自動的にテストが実行されるようにしておく
- テスト結果をメールやチャットに通知するとベター

### **失敗 : プロダクトコードのテスタビリティが低い**
**起きる問題**
テストを書くのがえらい大変になる

**処方箋**
極力ステートレスな仕立てにする

- 状態を持てば持つほど、テストを書くのは大変になる
- 状態と状態が相互に依存関係を持っていると、さらに大変になる
- 「引数をわたすと、一切状態の変更はせずに、戻り値を返す」という構造はテストしやすい
 - = 関数
- 関数型パラダイムの考え方
 - 初級編 : http://dev.classmethod.jp/server-side/language/func-program1/
 - 中級編 : http://postd.cc/an-introduction-to-functional-programming/

### **失敗 : 自動テストが完了するまでの時間が長すぎる**
**起きる問題**
本当はコミットのたびにテストを走らせて問題を早期に解決したいのに、それができなくなる
また、リリースまでの時間が長くなる

**処方箋**

目安として、20分～1時間程度で全テストが完了するように頑張る

- TDDの議論の中には、モックやスタブを活用してでも一瞬でテストが終わるのが良いという説がある
- 個人的には、実データや実コードから離れ過ぎることには懐疑的

### **失敗(スマホ業界限定?) : UIの自動テストを必死に書く**
**起きる問題**
コストパフォーマンスが悪すぎる

**処方箋**
長期的にペイするようなテスティングフレームワークが出てくるまで、様子を見る
また、UIについて、フルテストしようとしない

…この業界はまだUI自動テストの面で成熟していないのでは？

## 組織としての自動テスト導入失敗
### **失敗 : 最初っから『C0カバレッジ100%』という定量的な品質基準を立てる**
**起きる問題**
テストコードよりも、大量のモックと大量のスタブを用意するお仕事になる

 - 大量のモックと大量のスタブは、仕様変更とともに保守し続ける必要がある
 - コードカバレッジが100%だからといって、それが要件・仕様に準じているという保証はどこにもない
 - 得意先に「カバレッジは100%になりました、ただそのテストは、プログラマーが自分勝手に作ったテストです」とは言えない
  - ではその膨大なテストコードとモック・スタブを、品質レビューするのか？
  - すさまじい時間がかかるが、一体何を目的にしていたのかわからなくなってくる
 - つまり、かけるコストに対して、効果が低すぎる

**処方箋**
カバレッジは目安にする程度で、定量的な目標値を定めるとしても低めに設定する

- カバレッジ100%は、自動テストを初めてやる組織が目指す目標としては、敷居が高すぎる
- 自動テストをする前は、カバレッジなんて気にしてなかったのでは？
 - そのかわり、要件と仕様の網羅性を目視で確認していたはず。まずはそのレベルを維持するのがよい。

カバレッジに全く意味が無いという話をしているわけではなく、組織として優先するべきは、仕様でしょ？という話です。そっちの網羅が確認できたら、カバレッジ目標を立てていくのは悪くないと思います。
（ただ、100%まで厳密にやると、だいたい泥沼になる気はする)

### **失敗 : 仕様レベルではなく、プログラムレベルのテスト自動化を真っ先に始める**
**起きる問題**

 - 組織としては先に仕様の担保を優先するべきなのに、プログラムレベルのテストが足かせになって仕様がおろそかになる
 - プログラムレベルのテスト自動化を組織で導入すると、品質基準がピーキーになりがち
  - 『publicメソッド全てについて、境界値テストを実施する』
  - 『C0カバレッジ100%』
  - 『publicメソッドは全て詳細設計書に起こし、概要設計書との網羅性を確認する』
 - 記載量は、概要設計レベルよりも詳細設計レベルの方が多くなるのが普通なので、コストがハネあがる
 - 見積のレビューでは偉い人に「そこまでして、プログラムレベルのテスト自動化をする意味があるのか？」とか聞かれる
  - 答えるのが難しい
 - 見積に含んで発注が来ていればまだマシ
 - こうやって初回に作った基準は、以降維持し続けなければいけない。

**処方箋**
組織としてやるならまずは仕様レベルのテストから

- そっちのほうが、ボリュームが小さい

### **失敗？ : 『継続が確実ではない案件で自動テストを作ってもコスパが悪いため、導入しない』という判断**
**起きる問題**
継続が確実ではない案件は、だいたい継続する。

- ただし失敗かどうかは微妙なライン。半分くらいのケースでは正しいかも？
 - 自動テストに慣れない組織であれば、確かに初期コストは増えることが多い

**処方箋**
失敗しにくい自動テスト方法の教育

- 自動テストに慣れると、テストを書くコストはどんどん下がってくる

組織・個人に自動テスト導入での成功体験を稼がせる

 - 実際きちんと導入した自動テストは『あれがないともうやっていけない、無理』という資産になる
  - コスト面も、やってみると実は大幅に増えないことが、現場レベルでは把握できる
  - 『テスト仕様書とにらめっこしながらいちいち手で実施するより、コード書いて一発流す方が楽』
  - 『コードで書ければドキュメントの日本語のあいまいさを排除できるし再現性もよい』
  - 『誰が書いたか分からない、しかも日本語が怪しいテスト仕様書より、自動テストが一本残ってれば保守開発が楽」
- 現場が「今までとはテスト設計・実施の工数変えずに、リグレッションテストのコストはゼロにできますよ」と言える状況は、組織の説得に寄与する

## **組織としての自動テスト導入失敗のポイント？**
組織として担保するべきはまずプロダクトの外部仕様。品質保証の視点が重要になる。
それなのに、開発において「自動テスト」とか「ユニットテスト」という名前を持ち出すと、なぜか説明する前から詳細設計に対するテストだと認識される事例が多い。
だから本当は外部仕様のテストを自動化しようとしているのに、「内部仕様の担保に、とてもお金がかかる」と認識されてしまって、なかなか納得してもらえない。
 
## 個人・組織の自動テストが失敗した先に見えている失敗
### **失敗 : 個人・組織としての自動テスト失敗を積み重ねつづける**
**起きる問題**
組織も個人も、自動テストアレルギーにかかる

- 自動テストに防御反応を示すようになるため、その後自動テストを導入するのが難しくなる
- 治療には長い時間が必要

**予防方法**
自動テストの導入は地雷の宝庫ということを認識し、地雷を踏まないように丁寧に導入する

# まとめ

つかれた・・・ので一旦投下。

# 補足
- 『カバレッジ目標を品質基準にすることに意味がない』という話ではない
 - ただ、最初から目指すにはコストパフォーマンスが悪すぎる、という話。
 - コードカバレッジよりも先に、要件・仕様面の論理的なカバレッジが充足されるべき
- 事情が変わるケースは多々ある
 - 常に自動テストを書くのが正しいという話をしているつもりはないです
- 組織で導入する自動テストと、個人でやる自動テストは全然レベル感が異なる
 - 組織(プロジェクト)で導入する自動テスト(品質保証視点)
  - 組織として、プロダクトの仕様を担保するためのもの
  - だいたいのプロジェクトでは機能テスト仕様書を手で書いてしこしこ実施してますね？
  - 見積りには、機能テスト仕様書を作って、機能テストを実施する工数は必ず含むことができます
  - 仮にその工数内で、機能テストをコードに起こして、自動実行できるようにできると…
  　工数は変わらずに、再テスト(リグレッションテスト)の工数をほぼゼロにできることになります。
 - 個人で導入する自動テスト
  - じぶんが、安心するためのもの
  - みんなコード書いたら、安心するために手元でデバッグしますよね
  - 個人で導入する自動テストは、デバッグの代替えと考えると、分かりやすいです。
  - ifやforが3層くらいネストするコードは心配だから細かめにデバッグしますよね？
  - けど、単純に固定値を返すだけのメソッドはデバッグしないでしょ？
  - 個人レベルで自動テストを書くかどうかの基準は、そのロジックが心配かどうかで判断すると、機能します。