見出し画像

回帰テストのデススパイラル

原文:The Regression Death Spiral
作者:Blake Norrish

日本語訳:Yuta Tezuka

https://medium.com/slalom-build/the-regression-death-spiral-18f88b9fb030

開発現場において良くある物語

スプリント最後の週の木曜日の朝の出来事です。あなたは、小さな開発チームのQA担当です。チームにはまだ開発中の機能がいくつか残っています。金曜日の午後にデモを見せるために、金曜日の朝までに機能のテストの承認する必要があります。木曜日の夜は友人との予定がありましたが、その予定もキャンセルしなくてはいけないかもしれないと思っています。

デイリースタンドアップでは、エンジニアたちは「もうすぐ作業は終わります」と話していました。エンジニアがユニットテストやコンポーネントテストを開発の一環として行っていることを知っていますし、すでに何度か一通りの機能検証を行っているので、高品質のコードを手に入れることができると確信しています。しかし、テストに加えて、UIとAPIの自動化を行い、非機能要件を検証し、さらに健全性チェックも行う必要があります。一日に出来る作業量は限られているので、テストを終えることはできても、自動化を完了させることは非常に難しそうです。あなたは、この良くない状況をスクラムマスターに報告しました。

スクラムマスターから「わかりました。であれば・・」と言われ、あなたは次にどんな言葉が出てくるのか想像がつきました。スクラムマスターからは予想通り「テストの自動化を次のスプリントに回すのはどうだろう?」と提案されました。テスト自動化の取り組みは、「完了の定義」に明示され、開発物の中にも組み込まれ、常に見積もりの一部ではありましたが、結局後ろ倒しになってしまいました。「またか。」あなたの表情を見て、スクラムマスターが言いました。「私も後ろ倒しにするのは好きではないです。ただ、大きなマイルストーンを目の前に控えている今、チームの進捗状況には多くの人が注目しています。」

「わかりました。」あなたは笑顔で返答し、自分がチームプレーヤーであること、そしてチームの成功を最優先に考えていることを示しました。

物語はここで終わりです。

このような話は、多くの開発者にとって非常に身近なものだと思います。理由や文脈に関係なく、私たちは必然的に、テストの自動化を遅らせたり、減らしたり、あるいは単に後回しにするようプレッシャーをかけられる状況に陥ります。残念ながら、アジャイル開発におけるテスト自動化の重要性と、それを延期することの意味は、多くのステークホルダー、マネージャーなどには理解されていません。彼らにとって、テスト自動化は、「あると助かる」と思われる要件であり、締め切りが迫るとスコープから切り離すことを要望してくることが多いです。

私は、まさにこの状況にいました。そして、「回帰テストのデススパイラル」というフレーズは、いくつかのテストの概念をうまく内包し、ステークホルダーに不適切なテスト自動化のリスクを伝えるために有効であることを発見しました。もしあなたが、アジャイルチーム内で完全なテスト自動化の必要性を訴え続けているなら、この言葉をあなたの武器に加えることで、うまくいけば、私のように成功することができるでしょう。

回帰テスト

まず、「回帰テストのデススパイラル」の話に入る前に、回帰テストの考え方を理解する必要があります。機能をテストすることは、単に対象の機能をテストすること以上のものであるということを理解することが大切です。

ソフトウェアは、異なる時間軸で異なる人々によって書かれたコードに過ぎません。時には、そのコードは何年も離れて書かれ、決して会うことのない人々によって書かれることもあります。そして、これらのコードはすべて一緒に動作しなければなりません。これは簡単なことではありません。開発者は、複雑なシステムの構築を少しでも簡単に、リスクを少なく、そして予測しやすくするための戦略、アプローチ、パターン、テクニックを開発してきました。これらの戦略は、ソフトウェアの一部で行われた変更が、その部分以外では予期せぬ影響を及ぼさないようにするためのものです。

残念ながら、新しいコードを実装するときにどれだけ注意しても、変更が予期せぬ副作用をもたらす可能性は常にあります。新しい機能のためのコードが、システムの隣接する、あるいは全く関係のない領域の古い機能に何らかの影響を与える可能性もあります。ソフトウェアのコードは複雑に絡み合っています。新しい人や新しいチームによって新機能が以前の機能の上に重ねられているのです。その複雑性を考えると、予期せぬ副作用がいつでも起こる可能性があるのです。

以前動作していたコードを壊すような変更はバグ、デグレ等と呼ばれ、この種の不具合を検出するために専用のテストのカテゴリーがあります。それが回帰テストです。回帰テストは、以前動作していたものが正常に動作していること、新しいコードがアプリケーションの他の領域で予期せぬ副作用を起こさないことを証明するためのテストです。

したがって、ある機能をテストすることを、単にその機能をテストすることだと考えるのは短絡的な考えなのです。なぜなら、意図しない副作用が発生することを想定しなければならないからです。

開発中の機能以外の箇所に大きな変更を入れる場合のリスクと回帰テストの必要性は、周囲の関係者や開発経験のない人々によって見落とされがちです。機能をテストすることは、その機能が正しく定義された基準を満たしているか確認するだけだと言われる事もあります。しかし、残念ながら、品質を保証するためのコストを無視したり過小評価したりすることは、「回帰テストのデススパイラル」になる大きな原因になるのです。

アジャイル開発と回帰テストの負担

「回帰テストのデススパイラル」に関連するアジャイル開発の二つの概念を紹介します。一つ目は、アジャイル開発はストーリーという単位で機能を小さな個別の要件に分割するという点です。各機能は最小単位であり、単一の実体として開発しテストできます。アジャイル開発では、短い開発サイクルと小規模で反復的なリリースを推奨しています。アジャイル、スクラム、カンバン、SAFE、LeSS、ScrumBanなどの色々な開発手法が、それぞれの方法でこれらの概念を活用しています。

アジャイル開発は、仕様書によって定義された大きな機能から、それぞれ細かくスコープを分割された要件のストーリーに移行します。アジャイルのストーリーは独立して開発され、テストされ、レビューされ、承認されます。

小さな単位で独立しているストーリーを使って開発すると、ストーリーがそれぞれ独立して作られているような感覚になります。そのため、一つのコード変更がその他のストーリーのコードに影響される認識がされにくくなり、回帰テストの根拠として見えにくくなる可能性があるのです。確かに、ストーリーは独立していますが、これらのストーリーの下には、同じように相互に関わり合ったコードがあり、意図しない変更を引き起こすリスクも同じようにあります。ストーリーをどれだけきれいに分割しても、受け入れ基準をどれだけうまく定義しても、回帰テストはアジャイル開発において、ウォーターフォールプロセスで大きな機能を開発するときと同じくらい必要なのです。

これは重要なポイントであり、「回帰テストのデススパイラル」を理解するために必要なのです。それは、あるストーリーを変更したり、新しいストーリーを追加したりしても、他のすべてのストーリーの動作にマイナスの影響を与える可能性があるからです。小さな個別のストーリーへの移行は、作業の定義、理解、および並列化に役立ちますが、決してコードの分離を保証するものではなく、あるストーリーが他のストーリーに影響を与えるのを防ぐものでもありません。このような影響を特定するために回帰テストが必要なのです。

二つ目の概念に移りましょう。アジャイル開発は、小規模で漸進的なデリバリーを推奨しているということです。もし、6ヶ月ごとにアプリケーション品質を検証する必要があるだけなら、回帰テストのコストは、たとえ遅い手動テストであっても、それほど大きな負担にはならないでしょう。実際、アジャイル開発に移行する前は、この方法でソフトウェアを提供していました。回帰テストの計画を立て、6ヶ月ごとに、リリース前にこの計画を実行するのです。これは数日から数週間かかることが多かったのですが、6ヶ月のリリースサイクルの範囲内であれば、それほど負担にはなりませんでした。

短期間の繰り返しのなかで、徐々に規模を拡張していくアジャイル開発への移行は、品質の保証を担保する間隔を短くすることで、テストのコストを大幅に増加させます。現在では、回帰テストは2週間ごと、あるいは1週間に複数回必要かもしれません。これは、ウォーターフォール開発の時代からの大きな変化です。スプリントの長さがちょうど2週間なのに、2週間の手動での回帰テストを実行する時間のロスを想像してみてください。

手動での回帰テストのコストがどれだけ早くチームの開発速度に負担をかけるかを明確にするために、チームのスプリントの流れを見てみましょう。

新しく結成されたアジャイルチームが最初の開発スプリントを始めたばかりであると仮定します。最初のスプリントでは、テストするストーリーが三つあると仮定します。このテスト作業は非常に簡単で、三つのストーリーをテストするだけです。二回目のスプリントでは、さらに二つのストーリーを追加しました。これらのストーリーのテスト工数はどのくらいになるのでしょうか?単純な解答は、「最初のスプリントのコストよりは少ない」です。なぜなら、二つのストーリーに対してテストするだけだからです。しかし、実際にはこのコードの変更は、アプリケーションのどこかの領域に対してバグを引き起こす可能性があるというため、二つの新しいストーリーと既に開発している三つのストーリーの両方をテストする必要があります。

したがって、二回目のスプリントにおける実際のテスト工数は、開発するストーリーが 一つ少ないにもかかわらず、一回目のスプリントよりも多くなります。

ここで挙げた例は非常に単純化したモデルです。なぜなら、以前に完成したストーリーを手動で回帰テストするコストは、新しいストーリーを回帰テストするコストと同等であると仮定しているからです。もちろん、すべてを完全にテストする必要はありませんし、何かをテストするのは常に二回目の方が簡単です。しかし、回帰テストのコストはゼロではなく、複雑なソフトウェアでは、テストの初期コストと同程度のコストが必要なこともあります。なので、二回目のスプリントのかなりの時間が回帰テストに費やされるという点は変わりません。

この上記モデルを多くのスプリントを行なっているチームに当てはめると、新しいストーリーをテストするコストよりも、既に完成したストーリーを回帰テストするコストがすぐに増加しまうことがわかります。以前に完成したストーリーの数はスプリントごとに蓄積され、スプリントごとに全セットを回帰テストしなければなりません。

これは、アジャイル手法におけるテストの基本的な課題です。プロセスの漸進的、反復的な性質により、新しい機能およびストーリーが、数ヶ月または数年ではなく、数日または数週間の周期で、より迅速にシステムに追加されるので、反復ごとに膨大な回帰テストが必要とされます。ストーリーが完成するたびに、それをテストしなければならないだけでなく、以前に完成したストーリーはすべて、ある程度は再確認しなければなりません。手動回帰テストは、アジャイルのリリースタイミングの増加に合わせてスケールすることはできないのです。

これが「回帰テストのデススパイラル」です。既存のアプリケーションを適切にテストすることがスプリントごとに難しくなっていきます。全体のキャパシティに占める回帰テストを行う負担の割合が徐々に大きくなり、チームの生産性は窒息状態に陥ります。放置しておくと、チームの生産性は悪くなり、やがて何もできなくなってしまいます。それは新しい変更を加えるたびに、過去のストーリーをテストする数がどんどん増えていくからです。

アジャイル開発における自動化

前のセクションでは、テストが手作業で行われた場合、同じテストを再実行するコストは最初のコストの数分の一だというお話しました。しかし、実際にはもっと効率が良いやり方があります。ソフトウェアを使うことで、以前に実行したテストを自動的にテストする事ができるのです。
テストの自動化は、ユニットテスト、APIテスト、コンポーネントテスト、統合テスト、エンドツーエンドテスト、UIビジュアル回帰テストなど、さまざまなやり方が存在します。誰が書いたか、どのようにアプリケーションと相互作用するかなどは問題ではありません。すべての自動テストは既存の機能を検証するのに役立ち、過去に開発した機能が壊れていないことを証明し、手動の回帰テストの負担を軽減します。

また、テストの自動化は、初期テストの生産性をアップするものではないことを理解することも重要です。実際、テスト自動化の実装は、初期作業コストを増加させます。テスト自動化は、他の開発作業と同様に、アイデアと専門知識を必要とする開発作業なのです。自動化の作業はテストを行う事とは違います。自動化は、すでにテストされたものをプログラムでチェックする事です。これはまさに「回帰テストのデススパイラル」を緩和するために必要な要素なのです。

前の二つのセクションのポイント、つまりソフトウェアの変更は意図しない影響を与える可能性があること、そしてアジャイル開発は小さな変更の連続的な流れに過ぎないことを考慮すると、テストの自動化なしにはアジャイル開発が機能しないことは明らかなのです。テスト自動化なしにアジャイル開発を行おうとするのは、長い道のりを旅しているときに、ガソリンスタンドに寄ると遅くなるからと、ガソリンスタンドに寄らないようなものです。ガソリンは車の旅の基本的な部分であり、テスト自動化がアジャイル開発の基本的な部分であるのと同じです。テスト自動化は、回帰テストの息苦しい負担を軽減し、その結果生じる生産性の崩壊を避けることができる唯一の手段なのです。

テスト自動化を完全に無視し、テスト作業の全てを手動の回帰テストとして将来のスプリントに回すべきだと主張するステークホルダーやマネージャーはあまりいません。一般的には、チームは部分的または十分に自動化されたテストを受け入れるように圧力をかけられます。このようにしても、回帰テストのコスト全体の何パーセントかは、手動の回帰テストのコストとして将来のスプリントに渡されます。残念ながら、未完成の自動化は、どんなに小さくても、将来のスプリントでの回帰テストの負担を必然的に増加させます。

時間のかかる手作業による回帰テストを放置すると、「回帰テストのデススパイラル」につながるリスクを増やすことになります。もしあなたがストーリーをテストし自動化している場合、時間的なプレッシャーから、ステークホルダーに譲歩し、不完全な自動化でストーリーを完了としたらどうなるでしょうか?ある程度の回帰テストの負担は次のスプリントに引き継がれ、その次のスプリントでさらに多くのテスト時間が消費されます。このため、そのスプリントでは自動化のための時間がさらに少なくなり、自動化されるテストがさらに少なくなり、さらに将来に負債として引き継がれる、といった具合です。このパターンが何度も繰り返され、QAが自動化のための時間を無くし、手動テストで要件に追いつくために長時間労働を行い、最後にはQAがより成熟したアジャイル開発の実績のある会社への転職活動を行うまで、「回帰テストのデススパイラル」が続くのです。

私は、完全なテスト自動化なしに健全なアジャイル開発などあり得ないと強く主張します。テストを完全に自動化し、すべての形式のテスト(ユニット、コンポーネント、統合、UIなど)を網羅する自動テストのセットは、長期にわたって開発速度を維持するために必要です。完了の定義からこの要件を無くす提案は、それがどういう結果を生み出すか説明し抵抗する必要があります。短期的には成功したように見えますが、長期的な開発目標を達成することを危うくすることになります。

デススパイラル

話を最初の物語に戻しましょう。

デモの日を控えた木曜日の朝、あなたのチームにはまだ進行中のストーリーがいくつもあります。開発者は作業を終えており、コードはすぐにでもテストできる状態になっているはずです。しかし、残された時間はそれほど多くはありません。この悪いニュースを聞いたステークホルダーが緊急会議を行い、チームが目標の成果物を達成できるように、テスト自動化を延期することを提案しました。

あなたが抗議しようとしたとき、スクラムマスターが切り出しました。「それはできません!それは回帰テストのデススパイラルの一歩目を踏み出すことになります!」。
「自動化は常に完了の定義の一部であり、顧客向けのコードと同じように製品の一部です」と開発者は付け加えました。

「開発を成功させたいのですか、それとも成功しているように見せかけたいのですか?」ビジネスアナリストのこの言葉に、思わず顔がほころびます。
来週予定していた面接をキャンセルすることにしました。

謝辞
私は「回帰テストのデススパイラル」という用語を作ったわけではないですが、文章で満足に説明されたのを見たことがありませんでした。私が見つけたこの用語の最も早い使い方は、Lisa CrispinとTip HouseによるTesting Extreme Programming (2002)です。

その他参考文献
http://ivory.idyll.org/blog/software-quality-death-spiral.html (2008)
https://xebia.com/blog/regression-testing-with-an-agile-mindset (2010)

Thanks to Omar Galeano and Yuta Tezuka!


この記事が気に入ったらサポートをしてみませんか?