エビデンスとしてNUnitのGUIのスクリーンショットと、対応するテストコードが含まれている部分のVSのスクリーンショットを取る作業が終りません・・・
UnitTestのエビデンスって…なに?
一般的にテストのエビデンスというと、次の2点を指す。
- テスト手順を明らかにするもの(ex. テスト設計書、テスト仕様書、...)
- テスト結果の証拠(ex. 画面ハードコピー、DBスナップショット、...)
UnitTestでは、これはこのように解釈できる。
- テスト手順を明らかにするもの = テストコード
- テスト結果の証拠 = 今実行すればテストが全てグリーンになること
これがなぜか理解されず、軋轢とストレスと大きな工数追加になっている現場がずいぶんある。
なぜUnitTestはいつまでも理解されないのだろう。
余談。これらのことは、Seleniumなどを使って反復可能なテスト(一応結合テストにあたると思う)を実施する場合にも当てはまるようになる。
微妙に食い違う認識
テスト実施の時期に対する認識
テストを実施する時期…古典テスト方法では、これはいつでも「テストを実施した時点」であった。これがテスト自動化の登場・一般化で覆された。
自動化され反復可能になったテストでは、テストを実施する時期は「いつでも」になった。つまり「今動くこと」が重要になったのだ。
これがパラダイム・シフトになった。
従来の手動駆動テスト手法
- テストを実施したタイミングでのエビデンスが、必ず残っていた。
- そのエビデンスは必ずエラーを含み、実施を重ねるごとにエラーが少なくなっていく現象が見えた。
- テスト・エビデンスを追うことで、時点ごとに(スナップショット)「テストがどういう結果になったか」をチェックできた。
- 障害収束曲線(ほらこれだ!)を「計測」でき、それによって「テストの充実度」を間接的に観測できた。
反復可能なテスト自動化
- テストは常に動いているため、今現在のエビデンス(オール・グリーン)以外(要するに過去の結果)は重要ではなくなった。
- テストは常に動かせるため、コード作成とともにテストするように手軽になった。
- コード作成とテスト実施を同時に行うため、コードの完成と完璧なテストが同じタイミングで揃うようになった。
- 障害収束曲線は決定的に意味がなくなった。
コードというものが何かという認識
ここには、古くはクヌース先生等の時代から続く宗教戦争の歴史がある。
従来の、とくにウォータ・フォール型開発においては、コードというものは「最終成果物」であり、それは「製造」の結果でしかないとみなされる。
製造物のクォリティは「テストを通るか(条件を満たしているか)」のみが重視され、コード内部の整合性についてはほとんど重視されないようになった。
この視点で見た場合、UnitTestのような「コードで記述されたもの」は「余計(余分)な成果物」以外の何物でもない。
しかし一方でこれを戒め、「コードは一番詳細で唯一実行可能な設計書である」とする一派が従来からいた。この従来からある主張が、UnitTestの登場で大きく花開いた。
コードの設計書的側面が、テストによって補強される現象がおこった。
TDD/BDDでは、コードはテスト可能なまでに整理されることが前提となり、実施者は誰もが整理されたコードを書くようになった。(よく逆に考える論者(きれいなコードを書ける技術者しかTDD/BDDは実践できない)がいるが、それは違う)
そして、テスト・コードがインタフェースの詳細な仕様を表明するようになった。これがどのようなコメントよりも有言なことが確認されるやいなや、いわゆる詳細設計書を駆逐するのはおろか、コメントさえも駆逐しかけている。
何にバグが潜むのかという認識
ソフトウェア開発で不文律とされているものに「コードにはバグが存在する」というものがある。これには誰も異論がないだろう。
手動駆動テスト手法においては、その反復性の低さから「すべてのコードにはバグが存在する」という認識が強い。
バグが発生した時にそれを修正するとおこる、デグレードという現象もこの認識を補強することになった。
この認識に立っている人物は、UnitTestによるテスト・コードにもバグが潜んでいるのが当然だ、という見方をしている。テスト・コードは信頼できないものにしか見えない。
テスト自動化で反復可能になると、それが肌で感じられるほどに明らかになったことに「テストしていないコード・条件にこそバグが存在する」ということだ。
デグレードはヒューマン・ミス以外にはほぼ発生しなくなった。
この認識に立てると、コードの完全性をテスト・コードに依拠するようになる。そして、ガンガンとコード改善に手を出せるようになる。
テスト・コードにはバグはあるのか
はたしてテスト・コードにバグはあるのか?
以下の視点でみた場合、テスト・コードにしても相変わらず"バグ"はある、と言わざるをえない。
- テスト内容に抜けが発生する可能性がある。
- テスト内容に間違いがあり、その間違いを見逃す可能性がある。(偶然に結果が一致する場合に多い)
だが、これらはUnitTestだから起こるという類のものではなく、手動駆動テスト手法においても、十分起こり得るものだ。
そのため、これをもってUnitTestを無効だと主張するのは無理がある。
そして重要なことだが、次の点は完成した状態のUnitTestでは起こらない。
- テストが動かない。
- テストが誤動作する。
なぜ起こらないと宣言できるのか。それは、UnitTestにおいてはテスト・コードは100%通るからだ。カバレッジ率100%というのは実コードではなかなか難しい課題だが、テスト・コードにおいては誰でも完璧にその状態にできる。
そしてもうひとつ、テスト・コードは従来にないほど繰り返し実行される。この反復実行がまた、テスト・コードを頑強にする。
開発者は都合の悪いことには目をふさぐ?
よく言われる批判のひとつに、「開発者は都合の悪い部分をテストから無意識に排除する性質がある」というものだ。
だが、これらは原因がはっきりとしている。
- そもそも気づいていない。
- インタフェースに対する認識の低さから、インタフェースを満たすようなテストを行えてない。
- 反復性の低さから、一度通ったテストを省略する衝動に駆られる。
最初の問題はもう、どんなテスト方法をとっても検出はムリだ。
二番目の問題は技術者の一定の訓練・経験を必要とするが、UnitTestの方がダラダラ書きができなくなりインタフェースへの意識が高まるため、UnitTestを行うことでより強力に訓練できる。
三番目の問題は反復性を高め常時実効するUnitTestではおこらない。
どう立ち向かうか
味方にしてしまおう
まず、理解していない人には十分に反復テストを理解してもらう努力をする必要がある。
- UnitTestの場合は、それが「単体テスト」であり、手でやってもコードでやっても開発者が主体でテストしている事に変わりないことを理解してもらう。
- Seleniumなど結合テストの自動化の場合は、従来テスト設計書でシナリオを用意してやっていたことを自動化しているにすぎないことを理解してもらう。
そのために、次のように働きかけるのはどうだろうか。
- まずテスト・コードを見てもらい、どの部分がどのような事をやっているか概要を説明する。
- じっさいにテスト・コードを実行し、テストがすべて成功する瞬間を見てもらう。
- 目の前でTDDを実践し、テストが失敗から成功に移り変わる様子を見てもらう。
現場に近い人間ほど、上記を目の前にし「TDDを体験」することで認識を改めてくれる傾向がある。
残念ながら、現場から遠くなるにつれてその人物のアンテナの感度がより重要になってくるようだ。
CIサーバを立てよう
Hudsonのような本格CIサーバでもいいし、スケジューラでバッチを使ってAnt(NAnt)を定期的に実行するようにするのでもいい、またAntを手動起動するのも手だろう。
とにかく、次の手順を自動化してしまう。
- ビルドを実施する
- テストを実行する
- テスト結果をHTML形式で出力する(最近のUnitTestとAntの組み合わせではほとんどのもので可能なはず)
これだけで、テスト毎にエビデンスを残すとか障害収束曲線の判断元がほしいといった要請には対応可能だ。
また、Seleniumなどは画面ごとのスナップショットを保存するような改造もWeb上で公表されている。積極的に活用しよう。
結合テストを強くする
自動で結合テストをしようが、手動駆動テスト手法で結合テストをしようが、結合テストという視点は大事だ。
Agileではこの時に顧客サイドに使用シナリオを書いてもらう、リアルなテストを行うプラクティスがある。
個人的には、より結合テストを強固にするためには(予算との兼ね合いもあるが)、これに加え、やはり「危険な匂いを感じ取れる」職人気質なテスト技術者を招きいれ、シナリオ作成に参加してもらうのが良いと考えている。
またそのうえで、反復しないテストの実施を職人テスト技術者に行ってもらうのがより望ましい。
これをモンキー・テストと言ってバカにする傾向もあるようだが、やはり人間は、実際にアプリケーションを使ってみて初めて気づく、というものがある。
ただし、これを行う場合、テストに失敗したものは自動テストシナリオに組み込む、というタスクを忘れてはいけない。
最近の経験から
最近の経験で、技術者たちの頑迷な強い抵抗に合い、UnitTestを導入できない、という事があった。
仕方なくUnitTestを捨て、従来のようにテスト・フェーズを厚くする手法に切り替えた。(顧客の強い要望により、WF形式での開発だった)
その結果、何がおこったか。
メンテが難しいほどに長い「メソッド」の乱立。
バグを修正するたびに起こるデグレード。
いつまでも終わらないかに思えるテスト・フェーズ。
システムは苦心惨憺の末に大改造して納品した(あらためて書き上げたUnitTestと一緒に)。だが、短い開発期間だっただけに、この時の開発-テスト・フェーズは完全に失敗だった。
TDD/BDDを実践しようとしている人は、それが絶対的に正しい行為だと自信を持ってほしい。
結合テストの自動化を目論んでいる人は、それが絶対的に正しい行為だと自信を持ってほしい。
それはシステムを迅速に仕上げる助けになるだけでなく、システムの品質をより高みに置こうとすることなのだから。
ラベル:Agile応援団