iBet uBet web content aggregator. Adding the entire web to your favor.
iBet uBet web content aggregator. Adding the entire web to your favor.



Link to original content: https://nagise.hatenablog.jp/entry/2021/04/29/135959
なぜ自動テストの導入は失敗するのか? - プログラマーの脳みそ

なぜ自動テストの導入は失敗するのか?

 開発室の雑談。営業側のマネージャが言うには
「今のプロジェクトで自動テストの導入を試みている話をしたら、XXXさんのところでも過去にいくつか導入を試みたけどもみんな上手くいかなかったって話になって」

 なるほど?

 まあ確かに自動テストはシステム開発にとって魅惑の技法ではあるものの、では導入がうまくいっているか? というと普及率は低いと言わざるを得ない。私がお手伝いしたプロジェクトでは、元請け側から自動テストをやるお達しが来たわけだが、紆余曲折あって掛け声倒れのような状態になってしまった。

 ビジネス書の煽りタイトルのような本件だが、古式ゆかしき受注生産の業務システム開発プロジェクトに自動テストを導入しようとして失敗する事例を聞いたので、僕なりに分析して見出した要素を挙げておこうと思う。

V字モデル

 ソフトウェア開発の手法としてV字モデルというものがある。

 オーダーメイドでシステムを作るにあたって、どのような要求があるか、どう設計すればよいか、それを段階を踏んで細分化・詳細化していき、プログラムのコーディングに至り、そして単体テスト結合テスト、とパーツ単位の検品からその組み合わせとしての試験、といったように進めて成果を上げていくわけである。

f:id:Nagise:20210429133857p:plain
V字モデル概念図

CC 表示-継承 3.0, https://ja.wikipedia.org/w/index.php?curid=894431


 このV字モデルと、いわゆるウォーターフォール開発(定義や本来の意味は?といったことに踏み込むと難しいのでここでは触れない)において、自動テストの導入のネックとなるのはなんであろうか?


自動テストとは?

 自動テストとはこの文脈ではコンピュータ・プログラムが想定通りに動くかテストする工程を自動化するものだが、自動化といってもピンとこないかもしれないので簡単に触れておこう。全自動のコンピュータが何とかしてくれるという話ではない

 例えば、ふたつの数字を渡すとその和を返す機能を考えてみよう。

public int add(int x, int y) { … }

 これを試験する場合、ふたつの数字x, y にさまざまな値を渡して、得られる答えが期待通りかを確認することになるだろう。

  • 1, 2 を与えれば 3
  • -1, 1 なら 0
  • -2, -3 なら -5

 といった具合に。とはいえ、ひたすら並べていってもバグ検出の効果が薄いので、特徴のあるテストケースを用意するなどの工夫は必要だ。まあ、その辺の話も割愛。

 ともかく、こうした入力と出力があって、それが予見可能であれば、プログラムを呼び出してそういう値になるよな? ということを確認するプログラムが書ける。

assertEquals(3, add(1,2));
assertEquals(0, add(-1,1));
assertEquals(-5, add(-2,-3));

 こうした検証のためのプログラムが2021年時点のIT界隈で「自動テスト」と呼ばれるものの本体であって、「自動テストの導入」というのはこうした検証用のプログラムを書くことに他ならない。

 つまり、本来のプログラムのほかに、その検証用のプログラムを別途用意するというわけである。

 この検証用プログラムがうまく作れれば

  • 以後のテストは検証用プログラムを実行するだけで済む
  • 人力で膨大なテストを行うのに比べればコストダウンできるだろう
  • テストフェーズのリードタイムが短くなるだろう
  • 人力に比べヒューマンエラーが抑えれるだろう
  • 上記の総合的な作用により品質が向上するだろう

ということを夢見ていくつものプロジェクトが玉砕してきたというわけだ。


テスタビリティ

 先の自動テストのための検証用プログラムは非常に簡単な例だった。

 しかし、世の中にはテストしにくいプログラムというものがあって、検証用プログラムを書くことが非常に難儀することがある。ここでテストしやすさ、テスタビリティ(testability)という概念が出てくる。


 テストしにくいとはどういうことか? いろんなケースがあるが、テストしにくいモノのひとつには「状態を持つ」プログラムが挙げられる

 工学系の学部などで科学の学生実験とかやらされた人は身に染みていると思うが、「同じようにやって同じようになる」ためには、前提が揃っていることが大事である。

 状態を持たないプログラムを対象にして自動テストのための検証用プログラムを書くことは簡単である。同じ値をつっこめば、いつも同じ値を出力する。 1 + 2 = 3 であり、冪等(べきとう、何度繰り返しても同じ性質)である。

 しかし、内部で状態を持つプログラムは同じように操作したつもりでも突然違う挙動を示す。電卓でボタンを順に 1, +, 2, = と押した場合、3と表示されるだろうか? そのテストを始める前に誰かが 5, + と入力した状態から始めたらどうなるだろう? 期待される答えである 3 ではなく、8 が表示されてしまう。

 状態をもつプログラムのテストしにくさはこういうところで出てくる。まあ電卓の場合はCボタンを押してクリアしてからスタートすればいいわけだけども、じゃぁデータベースはどうしたら良い? データベースにリセットボタンが必要になってくる。データベースにテスト用のリセットボタン、用意してありますか?



 これは一例でしかないが、テスタビリティというのはなんとなくで上がる話ではなくて、テスタビリティを上げるための技法を駆使しなくては上がらないということの一端が分かっていただけただろうか。V字モデルでいえば基本設計、機能設計、詳細設計といった設計の工程で、あらかじめテスタビリティを上げるための配慮した設計をしておかねばならない。


自動テストはいつ作られるのか?

 冒頭に挙げたV字モデルの概念図では、要求分析に始まり、基本設計、機能設計、詳細設計と細分化してからコーディングを行い、単体テスト結合テストシステムテスト、受け入れテストと進んでいくように描かれている。

 オーダーメイドでシステムを作ろうとした際、こうした工程を経てシステムを作りましょうという提案がされることが多いことだろう。

 では、「自動テスト」を導入するとした場合、自動テストはこのV字のどこで作られるのだろう?



 単純に手動での単体テストを置き換えるものと位置付けるのであれば、V字モデルの「単体テスト」のフェーズで自動テストを作成するということだろうか? プログラムを一通り作り終えて、単体テストフェーズに移りましょう!というタイミング?

 😩😩😩

 この案、聞いただけでうんざりした顔をした人も多いのではないだろうか。



「導入を試みたけどもみんな上手くいかなかったって」

 そりゃそうだろう。うまく行くわけがない。



 いわゆるウォーターフォール開発の手法によって、書類で工程を分離して記録を残すやり方だと、設計フェーズと、コーディングと、テストフェーズは明確に分かれている。「分離することで上手くやってきた」と語る人もいることだろう。

 そうか、これが大きな阻害要因だったか。


自動テストが破壊するもの


 自動テストにはプログラムを「部分的に実行する」という意味合いもある。プログラムを書いて、動かしながら、その動きが狙い通りか確認しながら仕上げていく。この「動かしながら」のためには巨大システムの一部のパーツを、その一部分だけで動かす技法が必要で、自動テストのためのテスティングフレームワークがその答えの一つだ。

 プログラムを書きながら、自動テストのための検証用プログラムもまた並行して書いて、自動テストを用いることでプログラムを実行しながら進めていくというスタイル。その究極はTDD(テスト駆動開発)だけども、そうでなくともコーディング中から自動テストを書くのが良いやり方だ。

 早めに動かして、早めにバグを検出できるならそれに越したことはない。

 しかし、これはV字モデルにおけるコーディングフェーズと、単体テストフェーズを曖昧にする。従来のV字モデルのやり方で、明確に分離された単体テストフェーズに自動テスト作成を試みるようでは、自動テストの効能が十全には発揮されない。自動車を買って馬につないで牽かせるような話だ。



 また自動テストは、いかようなプログラムにも後付けで用意できるわけではない。これは先にテスタビリティの部分で解説した。自動テストをやるには自動テストしやすい設計である必要がある。極力ステートレスにするとか、モックに差し替えが可能なようにDIコンテナを駆使するとか。そこにはいろんな技法があって、そうした技法を凝らすことでシステムを自動テスト可能となるよう、先人たちが工夫を重ねてきた。

 それでも、より具体的なプログラミングを行っていると、後になってから「これはあっちに持って行ったほうがいいな」ということは多発するし、自動テストのために設計変更したくなることも生じてくる。これはV字モデル的にはコーディングフェーズから詳細設計フェーズへの手戻りだ

 また、自動テストの効能のひとつに「リファクタリングを可能にする」という大きなポイントがある。詳細設計フェーズとコーディングフェーズの分離はこの開発時のリファクタリングを大きく阻害する


 自動テストを活かすには、詳細設計フェーズとコーディングフェーズが分離されていてはいけない。コーディングフェーズと単体テストフェーズが分離されていてもいけない。



自動テストの導入のためには


 本稿の主張は、V字モデルこそが自動テストの阻害要因であるというものである。であるから、V字モデルに拘泥する限り、自動テストの導入は成功率が低いし、よしんば導入できたとしても効果が低く成果が上がらないものとなるだろう。


 おそらく、V字モデルでいうところの詳細設計~コーディング~単体テストのフェーズを再考する必要がある。この部分を大きく改革しないと自動テストの恩恵を得ることは難しいだろう。


 ある程度のスキルレベルにあるメンバーで行われるプロジェクトであれば、この詳細設計~コーディング~単体テストの一連の工程を、プロジェクトメンバーがうまいことやってくれていた。しかしこれは、そういう練度のプログラマで構成されたプロジェクトであればこそだろう。


 となれば。より低スキルの寄せ集めメンバーでベルトコンベアの流れ作業のようにシステムを作ろう! という思想でやるのであれば。少なくとも旧来のV字モデルの詳細設計~コーディング~単体テストに変わる新しい分業モデルを検討しなくてはならないはずだ。

修正履歴

  • 2021/04/29 自動テストのコードの値の誤りを修正