TDDはテスタビリティの保証をしてくれるのかも

 TDD Boot Camp 北陸行ってきました。

 TDDはテストドリブンデベロップメントの略で、自働テストを書いてから実装を書くというスタイル。ここでよく誤解されるのだけど、業務でおなじみ単体テスト結合テストといった網羅的なテストを記述してから実装を書くわけではない。目の前の1歩分、ひとつだけテストを書き、すぐさま実装を書いて自働テストをグリーンにする、というやり方をするのだ。こればかりは実際にやってみないと誤解は解けないかもしれない。

 さて、深夜のテストTL - Togetterや、TDDはテスト手法か否か - Togetterで議論されている「TDDは品質保証の手法ではない」という部分に関する議論。ここでいう「品質保証」はバグがないこと、ソフトウェア品質の12の属性でいう信頼性(reliability)が高いことを指す。

 TDDのスタイルには網羅的な検査をしてバグをあぶりだすようなフェーズがない。ホワイトボックステストのような条件分岐を加味した網羅的なテストを作るわけじゃない。だから、網羅的にテストしてバグのないことを保証しようという考え方とイコールにはならない。もちろん、TDDをやりながらテストを順に網羅していくことはできる。TDDと品質保証はおおよそ直行する概念のように思われる。

 じゃあTDDは品質保証をしないならなんなんだ、という話なのだけど、試験性(テスタビリティ:testability)を保証するものなのではないだろうか。

レガシーコードとの戦い

 TDD Boot Camp 北陸の2日目ではレガシーコード、つまり自働テストのないコードに対してテストを作るというTDDへの移行で最大の壁となるであろう部分を体験学習した。

 「お客様のなかにレガシーコードをお持ちの方はいらっしゃいませんかー」
 「僕のでよければ提供するけど」

 そんなわけで拙作のHEXリバーシを検体として提供したわけだ。これは2008年ごろにジェネリクスでどこまで抽象度を高められるかをテーマにHEXフレームワークを作成した時の習作。HEXマインスイーパと対をなす作品となる。この2つのまるで違うゲームを乗せれる抽象度の高さをジェネリクスを活用したフレームワークで実現している。

 そういうシロモノなのでオブジェクト指向的な抽象度には相応に自信があったし、DIコンテナが与えたテスタビリティ向上も参考に組んだのでテスタビリティはそれなりに高く設計されている…つもりだった。

抽象度が高いだけじゃだめだ

 課題はレガシーコードに機能追加をする、そのためにまずレガシーコードに自働テストを加える、というシナリオ。HEXリバーシはAI対戦専用なので二人プレイできるように機能追加する、というシナリオではじめた。スタイルとしては「コーディング道場」 (Coding Dojo) 方式*1を採用。厳密にやっていたわけではないけども。

 検体を提供してみんなで集まって*2いざテストを書こうとすると頭を抱えてしまった。

 該当の修正を行う場合に影響が出そうな既存コード周辺に集中的に自働テストを加えたい。加えたいのだが、テストを行いたいところで都合良くメソッドの構造化が行われているわけではない。また抽象度が高いからモックを用いたテスト(DIコンテナを使った開発ではお馴染のあれだ)を使えなくはないものの、モックが壮大になりすぎる。

品質属性の相互作用と高い壁

 ここで品質属性の相互作用を見てみよう。ソフトウェア品質の12の属性では書籍「ソフトウェア要求」の228ページから引用して以下の表を掲示していた。左の属性を高めるとそれぞれの属性に対して+や-の作用を及ぼす。その関連性を示す図なんだ。

可 用 性 効 率 性 柔 軟 性 完 全 性 相 互 接 続 性 保 守 性 移 植 性 信 頼 性 再 利 用 性 堅 牢 性 試 験 性 使 用 性
可用性                  
効率性      
柔軟性          
完全性            
相互接続性              
保守性            
移植性        
信頼性        
再利用性      
堅牢性              
試験性          
使用性                

 抽象度の高い設計というのは、柔軟性(flexibility) = 拡張のしやすさ、保守性(maintainability) = バグの修正しやすさ、再利用性(reusability)が高くなる。そうすると、品質属性間の相互作用として、どれも試験性には+の作用をもたらす。

 これが、僕のテスタビリティはそれなりに高いだろう、という思いの元だったのではないか。

 そして、柔軟性、保守性、再利用性を考慮した設計は、試験性によい作用をもたらすけども、試験性そのものを良くしようという設計とは一線を画す。この間にある壁が高く高く僕らの前にそびえたったわけだ。

TDDはテスタビリティを担保する

 TDDというスタイルは、自働テストから書き始める。テストが書けないことには実装を書けない。それはつまり、試験性(テスタビリティ)が考慮されたコード設計になっていることの保証になるのではないか。

 どんなに柔軟性、保守性、再利用性が考慮されようとも、後付けで自働テストを加えるのは難しい。レガシーコードはテスタビリティが低いのだ。

TDDで「品質が上がる」

 TDDでは信頼性、つまりバグのないことは保証できないと冒頭述べた。でも、TDDが信頼性の向上に寄与していると感じる開発者は多いだろう。しかし信頼性そのものを上げるわけではない。

 このもやもやは品質属性の相互作用で説明できるんじゃないだろうか。試験性の向上は信頼性に+の作用をもたらすのだ!

 TDDは試験性を直接的に向上させる。信頼性は試験性の向上により間接的にじわりと上がるのではないか。

謝辞

 TDD Boot Camp 北陸の開催に尽力してくれた id:katzchang
 講師をしてくださった id:t-wada (TDD Boot Camp 北陸に登壇させていただきました - t-wadaの日記)
 参加してくださったみなさん

 本当にありがとう!

 「東京者の義務を果たさないと」と雪と戯れる id:t-wada は様式美をおさえておられるナイスガイです。

*1:TDDを根づかせる:導入の問題と解決策に解説がある

*2:Javaチームはこの課題。他の言語チームはまた別のレガシーコードと戦った