今回は、モデルから一段階昇華させていきます。ちょっと難しいかもしれませんが、一部、試験の範囲になっているものもあるので、勉強がてら読んでみてください。
モデルのひとつの結晶、塊のことを「モジュール(module)」と呼びます。似た言葉なので、一度聞いただけでは、訳が解らないでしょう。
これは、化学に例えるとわかりやすいかもしれません。原子(atom)が、モデルだとするならば、分子(molecule)がモジュールと言えるでしょう。
ソフトウェアとモジュール
モジュールは、ソフトウェアの大事な構成単位です。モジュールが組み合わさってソフトウェアになります。ソフトウェアを構成するどのモジュールが欠けても、基本的にソフトウェアは動きません。チームでソフトウェアを作るときに、プログラマごとに作成するモジュールを割り振ります。プログラマは、与えられたモジュールの作成に勤しみます。そして、それぞれ、完成したモジュールを組み合わせてソフトウェアが完成するという流れです。
ソフトウェア構築の詳しい流れは、共通フレーム2013に書かれています。一度目を通しておくとよいでしょう。
ユニットテストは書くべきか
基本的にモジュールでソフトウェアが構成されます。では、「きちーっと設計書通りモジュールを作りましょう」で基本的に議論が終了します。でも、これで終わってしまったら、読者が怒り出すかもしれないので、もう少し深堀していかなくてはいけませんね。
最近、ブログやツイッターで困った議論が勃発していて、もはや炎上といっても過言ではないでしょう。その議題は、「ユニットテスト(unit test)は書くべきか」です。
ツイッターでの議論は、基本的に長く書けないですし、みんな他人事なので、まともなレトリックを使って書いているわけではないでしょうが、ひどいなと思うことが一つあり、後ほど述べていきます。
ユニットテストとは
ユニットテストとは、作成したモジュールの動作が正しいかどうかを、人間による目視でチェックするのではなく、プログラムによって判定する方法のことを言います。モジュールへの入力に対して、こうあるべきだという出力をテストとしてプログラムします。そして、実行すると、記述したテストが順番に実行されていきます。テストに通ると緑色、通らないと赤色の点が画面に出力されていきます。
赤い点が、緑色に変わるまでモジュールを改善していきます。ここまでが、ユニットテストを使った開発手法の概要となります。
今のところメリットが多いように感じることでしょう。目視でのチェックがいらないですし、機械的にやってもらえれば工数が減るような気がします。
しかし、デメリットももちろん生じるのですが、これに関しては後述します。
二元論に陥る愚かなプログラマ
ユニットテストに関して勉強した後に、もう一度先ほどの「ユニットテスト書くべきか否か」の議論に戻ります。世の中の議論には、二元論というのがあります。シンプルの極みである二元論というのはどのようなものでしょうか。
「人工知能によって仕事がなくなるか否か」「成功するにはポジティブであるべきか否か」「今日の昼飯はラーメンか牛丼か」「ラーメンならば、醤油か塩か」
こういう二元論に陥るのは、社会やプログラムを設計する人にとって非常に危険なことです。多様な社会や、プログラミング手法は、無限次元で構成されているはずです。それに対して、二元論というのは、一次元の議論です。二元論に陥るということは、「たったの1次元しか僕らは考えられません」と自分の能力の無さを露呈することになります。
第一線で活躍しているプログラマもこぞって「書くべきか否か」の二元論争に巻き込まれているのを見て、なんだか悲しいなというのが僕の感想です。
どうあるべきか
まず、いきなりユニットテストに目を向けるのでは落とし穴にハマる確率が高いです。もっと、高い視点で物事を見ていかなければいけません。ソフトウェアは、通常作ってリリースしたら終わりではないので、数年から数十年のマイルストーンにおいてどのような運用をしていくかを考慮した上で、設計していかなくてはいけません。
その中で、ユニットテストがどの程度工数を掛けられ、どのような成果がでるのかを見極めなければいけません。ユニットテストは無償でできるものではなく、かならず工数に上乗せされるものです。そのコストに見合ったものになるかは、数年経たないとわからないかもしれません。きちーっと見積もる必要があるでしょう。
そして、ユニットテスト自体も運用していかなくてはいけないことを忘れてはいけません。仕様変更するたびに、ユニットテストの内容も変わっていくことでしょう。その度に、工数が掛かることを常に忘れてはいけません。
この時点で軽く数次元の考えるべき要素が頭の中で思いつくことでしょう。二元論に陥るとこのような思考はできなくなります。
ドライバとスタブ
ここで、ドライバ(driver)とスタブ(stub)に関して復習してみましょう。どちらもテストにおいて必要になる知識です。作ったモジュールに対して入力して、出力を受けとるプログラムをドライバと言います。ドライバによってテストしたいモジュールを動かす(ドライブする)のです。ドライバによって入力した値が、モジュールを通じて変換され、適切に出力されるかを見ればよいです。
図式にすると、ちょうど、真ん中にテスト対象のモジュールが来て、周りをドライバが囲んでいるような形になります。このようにして、真ん中のモジュールが周りのモジュールと協調しあって動くことを確認できます。
次にスタブですが、ちょうどドライバと逆の概念になります。先ほどの真ん中のモジュールが仮の動きをスタブとして振る舞い、入出力を行います。
入力側のモジュールと、出力側のモジュールが完成している場合に、それぞれのシステムが協調しあって動くことを確認することができます。
図式もドライバのものとは逆になるでしょう。真ん中にスタブが来て、周りに作成したモジュールが配置されたような形になります。
テストをするには
ドライバとスタブの話をしたのは、ユニットテストでも使うからです。イメージしていただいた方にはお解りいただけたと思いますが、テストをするには、各モジュールの入力と出力が明確に定義されている必要があるということです。入力と出力が定義されていないものは、ドライバやスタブによってテストすることができません。なので、テスト不能ということになります。入力に対して出力が曖昧なもの、または、頻繁に変更されるようなものは大変です。
例えば、Rails などのフレームワークでも、テストのための便利なライブラリを入れることができます。そして、関数やメソッドの動作チェックはもちろんのこと、ブラウザ上の UI を操作した時の挙動とかもチェックできます。とても便利ですね。
このように便利なライブラリがあるので、何でもテストに落とし込んでしまいがちです。しかし、UI の操作などの頻繁に変わるものや、ユーザによって表示が切り替わるものに関しては、よく考えてユニットテストを実装しないと後々大変なことになるでしょう。
ソフトウェア開発におけるコスト感
ソフトウェア開発において、一番大切なのはプロジェクトの成功です。ユニットテストを書くのは、あくまで一部でしかないということです。プロジェクトの成功のためにあえて、ユニットテストを書かないというのも一つの選択かもしれません。期間、納期、予算、工数、人員、技術などあらゆるパラメータを考慮した上で、ユニットテストを書くべきかは考えるべきでしょう。そして、「なるべくユニットテストを書くこと」という曖昧なものであれば、基本的に、後々ユニットテストは運用されなくなるので止めたほうがよいか、仕様を満たすために作ってすぐ捨てる程度のものとして捉えたほうがよいでしょう。
ユニットテストを書く範囲に関して明確な指針をつくることです。設計書やタスクごとにどの範囲でテストを書くべきか・今後も運用するのかをきちーっと明記することです。下手に運用するとユニットテストが仕様変更に追いつかなくてプロジェクトが破綻するという事態に繋がります。
何事もメリットとデメリットのトレードオフによって成り立っています。それをきちーっと理解したうえで生活していきたいものです。
ではまた来週。