#背景
仕事の関係で、ATDDを導入して、Xヶ月間ぐらいを回してました。それを機に現時点にATDDに関する感想を書きます。
ATDDとは
ATDDはAcceptance Test Driven Developmentの略で、開発手法の一種です。(#@-DD, #@ driven developmentというの非常に流行っていますね…)
〇〇-driven developmentは直訳すると、〇〇駆動開発です。ATDDにおいて、その駆動になるのはAcceptance Test、すなわち受け入れテスト。
いろいろ名詞を並べても、全然伝わるものではないので、下記の2点を強調します。
ATDDもテスト駆動開発(TDD)です
前少し言及しましたが、開発の駆動となるのは受け入れテストです。受け入れテストは出荷判定の際に使うテストのことで、受け入れテストが通ったら、出荷して良いという一見簡単な概念です。
テスト駆動開発である以上、開発の流れは下記にようになるでしょう。
ATDDは仕様ぎめから始めます
これは当たり前と思われるかもしれませんが、(仕様を決めなければ何を開発するんだ?)
ただATDDで決めた仕様は受け入れ基準となります。その受入基準はまた受け入れテストのもととなります。
流れは以下のようにます。
(またここで仕様という言葉を使っていますが、ATDDはuser storyという用語を使っています。仕様とuser storyは本来違うものになると思いますが、一旦それは深く議論しません。)
成果物のイメージ
1つのscenarioは1つのテストケースという感じです。
scenarioはatddのツールで実装したe2eテストと紐付きます。それによって、決めた仕様がテストとmappingされます。
導入目的
ATDDという開発手法を提唱する人たちが何かしらの課題を解決したいに間違いないんですが、一旦それを置いておきます。主に自分がATDDを通じて解決したい課題を説明します。
1. ドキュメントとコードのずれが頻繁に起きる。またそのずれを検知しようがない
仕様書やら要求書やら、そういった仕様を表現するドキュメントを書いたことありますよね?
開発手法によって、仕様書を書いて、それをもとに開発するとこともあるし、開発が終わってから、人間が理解できかねるゴミ実装を解読するコストを減らすために、書くところもあるでしょう。
何れにせよ、同じものを表現したいのに、ドキュメントとコードが二重管理になります。
また二重管理である以上、仕様書とコードの同期が必要で、それの同期を保証できる仕組みがないと、ズレが生じる。
そうしたら、いろいろな問題が生みます。
経験上、ドキュメントとコードのズレが大きくなって、信用できないドキュメントになったり、ドキュメントの仕様cover率が低かったり、ドキュメント自体が廃れることがあります。
ATDDを導入するとこによって、書いた仕様書(受け入れ基準)と受け入れテストが連動されるので、ズレが生じにくいです。
ATDDで開発すると、まず受け入れ基準を作成・変更するところが始まって、従来に当たるドキュメント更新が開発流れの一環となり、ドキュメントの更新漏れが起きにくいです。
仮に受け入れ基準を更新せずに、実装してしまうと、受け入れテストが落ちることによって、その漏れが検知できます。
2. degradationの早期検知
それはtddとか、test firstとかと同じく受けられる恩恵です。
問題のある実装してしまい、他の機能に影響を及ぼし、仕様どおりに動かなくなることが早く発見し、対応できます。
3. リリースする際、手動で動作確認することがもったいなくて、そのコストを減らしたい
リリースする際、プロダクト最低限担保したい機能の動作確認を手動でやったことはありませんか?
ただの単調な作業で、人間にやらせるべきことではないと思ったことはありませんか?
プロダクトの成長に連れて、確認したい項目が多くなるでしょう。またリリースサイクルできるだけ短くしたいでしょう。そうしたら手作業どんどん増えて、心理的に辛い作業になります。
そういう手作業を自動化することによって、だいぶ開放されるはずです。
#実践
上述した課題を解決するために、ATDDを導入して、回してみました。
使用するフレームワークを軽く説明します。
- atdd: cucumber
atddの一般的なフレームワークを使っています。
- e2eフレームワーク: selenide
e2eを書こうとしたら、javaという言語が前提であれば、seleniumが使えますが。ただseleniumはテストクレームワークではないので、テストを書くのはつらいです。selenideはseleniumをうまいことwrapして、テストに特化するlibraryで、使い心地がseleniumよりよいです。
#良いところ
狙った課題解決ができました(かなぁ...)
- 受け入れ基準がコードとのズレがほぼ無く、信頼できるドキュメントになります。(仕様を間違えて、その実装も間違えて、テストが落ちないという奇跡的なことは一応起きたことがあります > < )
- 手動の動作確認はだいぶ減ります。
- また開発者でない人たちが受け入れ基準を見て、プロダクトが何できるかは把握できます(推測)
#気になるところ
「利点はそんなに語らず、これからは全部気になるところなのか」と思われるかもしれませんが...
開発コスト
課題解決ができるというものの、決められた期間(特に短期間)で、課題解決のための投資と比べたら、費用対効果はなんとも言えません。プロジェクトが永遠に続くなら、絶対恩恵受けられるに違いないんです。しかも長ければ、長いほど、恩恵が大きいです。(誰もわかるでしょう)
e2eテストの実装が単純に工数かかる
正確な数値がないんですが、肌間でという、最初のところは学習コストがあるため、普通の開発より3,4倍の工数がかかります。落ち着いたら、1.X倍ぐらいになるでしょう(まだ実現できていません。)
簡単にいうと、その実装工数と手動の動作確認工数がどちらが大きいかを明確にすれば、検討できるでしょう。
e2eテストの実装スキル
テストの実装は実際結構お作法的なところがあって、適切な実装をやらないと、機能がデグレしてないのに、テストが落ちることがよくあります。
例えば、seleniumは下記の制限があります。
画面上表示されない要素に対してクリック操作をしようとしたら、エラーになります。
画面上の要素が増えていくうちに、要素が見えなくなったら、テストが落ちます。
こういう場合は、機能のデグレというより、テストのデグレというのは適切でしょう。機能の実装が問題ないのに、テストが失敗するなんて、何なんだという気持ち悪さ湧いてくるでしょう。頻繁に起きると結構テストのデグレ対応のために、工数をかけないといけません。
開発リズム
e2eテストの実行時間がunitより長く、1ケースだけ動かしたくても、20秒以上かかります(主に立ち上げの時間が長いです)。
unit testでtddになれる人にとって、その実行時間が長すぎて、開発リズムは全然良くありません。
本来tddのred -> green -> refactorというサイクルをできるだけ早く回して、開発するという手法は工夫しなければ、現実的ではないところがあります。
unitテスト、redケースを書いて、5秒でケース実行して、その結果に応じて修正します。atddの場合、redのケースを書いて、ケースを動かしてから終わるまで、時間的にコーヒーいっぱいも淹れられるでしょう。
また工夫しないと、ケースがいっぱいできた時、全ケースを流そうとすると、非常に時間がかかります。
受け入れテストの粒度と範囲
仕様や機能のどこまでatddで担保するかは悩ましいです。
理想なら(時間が無限にあって、テストの実行時間が無視できる程度)もちろん全部atddが書けばよいでしょう。ただその理想が実現される日より、コンピューターがすでに進化して、プログラムを書く人間すらいらなく、勝手にプログラムを作れるようになった日が先に訪れると思います。
考えられる対策は2つがあります。
- とにかくatddでe2eを実装する。実行時間とかの限界を感じたら、テストをe2eからunitに落とします。
- 予めatdd, unitの守備範囲を決める。ただそれは結構経験が必要です。
#まとめ
気になることろをいっぱい書いたんですが、学びのあることを示していると思います。現時点答えのない課題もありますが、今後試行錯誤して、より良い開発ができればと思います。