FURYU Tech Blog - フリュー株式会社

フリュー株式会社の開発者が技術情報を発信するブログです。

リグレッションテストを整備した効果と今後の課題

こんにちは。ピクトリンク事業部でQAだったりweb開発だったり色々している橋本です。
今回は、iOSアプリ開発においてリグレッションテストを整備して得た効果と、今後の課題について書いていきます。

リグレッションテストとは?

リグレッションテストは、新機能の追加やバグ修正を行った際に、既存の機能に影響が出ていないかどうかを確認するテストです。
サービスやアプリのシステム全体を対象に、 改修した箇所を問わず 全機能の検証を行います。

システムが育ち、増改築を重ねて複雑性が増していけばいくほど、改修による影響範囲がどんどん読めなくなっていきます。
複雑化・大規模化が進行していくと、小さな修正のはずなのに思わぬところに影響が出たりします。デグレ(Degrade)リスクの拡大です。

たとえば「ログインまわりのデザインを修正しただけのはずが、課金解除のボタンが表示されなくなった」みたいなことが起こり得ます。
あるいは眼精疲労に効く足ツボがあるみたいな感じです。

複雑性が増してこんがらがり、どこがどう繋がっているかわからなくなっていく。つらい。

個人的には、長期に渡って開発・運用されるプロジェクトにおいて、これは遅かれ早かれ避けられぬ問題だと思っています。投げた石がいずれ地面に落ちるように。太陽がやがて沈むように。開発を続ける限り、少しずつでも複雑性というものは降り積もっていくものでしょう。
この問題への対策のひとつがリグレッションテストです。

リグレッション(Regression)は「後戻り」や「退行」、「回帰」といった意味合いの単語で、進行(Progression)の反対語です。
リグレッションテストは、そのまま直訳して「回帰テスト」と呼ばれたりもします。
デグレードテスト、あるいはノンデグレードテストと呼ばれるテストもリグレッションテストと同じものです。エイリアスてきな。
開発現場によっては「リグレッション」よりも「デグレ(デグレード)」のほうが聞き覚えのある単語かもしれませんが、デグレードは和製英語なので日本国内でしか通じない言葉とのことです。ノートPCみたいなものなんですね。
ノートPCは国外ではLaptop PCと呼ぶのが一般的だとか。

リグレッションテストの具体的な目的は、次のとおりです。

  • 想定外の箇所に影響が出ていないことの確認
  • 古いコードをベースに改修したため、コンフリクトの解消時などに修正が巻き戻っていないことの確認
    • 複数のラインで開発が動いている場合に起こりやすい

もともとうまく動いていた機能が動かなくなるなど、機能低下、品質低下を検知するためのテストってわけですね。
実際に整備してみると副次的な効果もありました(※ 後述します)が、メインの効果はこんな感じです。

リグレッションテストの亜種には、見た目の変化を検査するVRT*1(Visual Regression Test)と呼ばれる手法もあります。

リグレッションテストを整備する上で重視したこと

私たちのプロジェクトでリグレッションテストを整備するにあたり、とくに重視したポイントです。

サービス全体を広く(浅く)カバーすること

ユーザが利用する機能を一通り試し、想定外の変化が出ていないかを確認します。
ただし、境界値チェックや異常系のチェックは最低限だけ行い、 ユーザが利用しないデバッグ系の機能は省きます。深さや網羅性よりも広い範囲をカバーすることを重視しました。

具体例として、複数の要素を選択できる機能がある場合を仮定します。厳密に境界値をチェックするなら、

  • 何も選ばないパターン
  • 1つだけ選ぶパターン
  • 2つ選ぶパターン
  • 上限個数まで選ぶパターン
  • 上限個数を超えて選ぼうとしてみるパターン

などがテスト項目として考えられるでしょう。もしかしたら、

  • 上限個数まで選択対象がないパターン(100個が上限個数なのに20個しか選択肢がないなど)
  • ひとつも選択対象がないパターン
  • 選択してから解除するパターン

もチェックしておきたいかもしれません。

ただ、こんな調子ですべての境界値を網羅的にチェックしていくと、テスト項目が膨大になり、テストの実施に時間がかかりすぎてしまいます
千年を生きるエルフならいざ知らず、時間も予算も有限です。現実的な問題として、テスト項目の網羅性と、工数のバランスを取る必要があります。バグを殺す魔法がほしい。アプリを殺すバグならある。つらい。

そのため私たちのプロジェクトでは、リグレッションテストにおいては「深さ」よりも「広く浅く」を重視しました。
上記の例で言えば「上限個数を超えて選ぼうとしてみる」「上限個数まで選ぶ」のパターンをチェックします。

最低限、すべての機能が使えること を担保する取捨選択です。
テストケース自体の運用・調整コストを下げるため、仕様変更がほとんどない主要機能を主軸として、リスクの高い機能(ユーザ影響がとくに大きかったり、バグの出た実績が多いものなど)に関しては手厚めにします。

ひとまずは1時間で手動実行できる規模を目安にテストケースを構築しました。
また、確認効率をあげるために、テストを行う順番や粒度も調整します。

ちなみに、より網羅性の高いテストはリグレッションテスト以外のタイミング(リリースの最終確認など)で別途実施しています。

私はリグレッションテストのことを、いわば味見のようなものと考えています。
「明らかに変な味になっていないかどうか」が確認できれば良く、鍋の底まで余すことなく食べ尽くす必要はない……みたいな。

ある程度のコストで、ざっくりいい感じな確認が取れればいい

実施タイミング

不具合を早期に発見し、手戻りを抑制するため、リリース用のブランチにマージする前 に実施します。

これまでの流れでは、開発者が修正範囲の確認をしてリリース用ブランチにマージ、リリースする分の修正や機能追加が出揃ったらQAが品質の最終チェックをするというものでした。
しかし、この流れではQAが確認するまでアプリ開発者は問題の発見・修正が行えず、お互いに待ち時間が発生したり、問題が見つかったときにリリーススケジュールが大きくズレ込んだりしてしまうことがありました。

これらの課題を受けて、リグレッションテストをマージ前に開発者自身の手で実施し、問題の早期発見および手戻りの抑止を目指しました。

動作確認をどこまで行うかについては、これまではそれぞれの開発者に委ねられている範囲でした。個人の感覚や経験に依存していたわけです。
そこに、テスト項目を明確に設定して「ここまでやったからOK」という基準を作りました。
また、リリース用のブランチにマージされていろいろな変更が組み合わさる前に確認することで、問題箇所の特定や修正が比較的容易になります。

さらに副次的な効果(※ 前述のものです)として、開発者が機能ごとではなく全般的にサービスを触る機会が増えたため、使い勝手や見栄えなど、ソフトウェア品質でいう使用性に関する意見が出やすくなりました。

より良いサービスを目指すためには、ユーザと同じ目線に立ってサービスを使うのがとても重要です。
開発者は、自分たちが機能開発していることもあって、単一の機能の仕様や動作については熟知しています。ここでさらにサービス全体を通して体験してみると、より広い視野が生まれます。
機能単体ではまとまっていた動作も、他の機能との噛み合わせや、導線の確保などを含めた使い勝手の良し悪しを加味すると、さらなる改善の余地に気づけたりします。
リグレッションテストのサービス全体を広くカバーする特性がうまく合致したと言えるでしょう。

テスト項目のメンテナンス

月1回を基準に、テスト項目や運用方法の見直しを行いました。
新機能の追加や既存機能の変更に伴ってテスト項目が増えたり変わったりするほか、実際にテストを行ってみて「この項目はコストが重すぎる」とか「これで代替できないか」といった意見を盛り込むためです。

また、カバーできる範囲だったはずなのにすり抜けてしまった不具合も、次は取りこぼさないように、項目の追加や調整を行います。

導入当初は所要時間の長い試験項目や、準備に手間取る項目があったので、テストに慣れた人が実施しても合計1時間以上掛かっていました。
実施コストが重いというフィードバックを受け、確認範囲を狭めたりなどの調整を行い、現在では30〜40分で一通り終えられる程度のテスト項目で運用しています。

整備後の効果と今後の課題

リグレッションテストを整備したことで、当初の目論見通りデグレによる手戻りが減りました。また、プロジェクトメンバーの品質を保つ共通認識の醸成にも役立ったと感じています。さらに、開発者もサービスを全般的に動かすことによって使い勝手などへの理解が深まり、改善意見が出やすくなりました。

課題としてはテストの実施コストが重いことが挙げられます。
変更をリリースブランチにマージするたびに繰り返し実施する必要があり、しかも機能が増えればテストする項目も増えるために、どうしても実施コストが重くのしかかってきます。
この課題を解決するため、私たちは現在リグレッションテストの自動化に取り組んでいます。今のところ、ツールはMagicPodを利用しています。

magicpod.com

MagicPodはクラウド上に実行環境を用意でき、GUI操作でコードを書く必要がないためエンジニア経験が少なくても運用できます。
テスト結果をSlackに通知したり、テストアプリのデプロイからテスト実行までをコマンドラインで完結できたり、CI/CD(継続的インテグレーション/継続的デリバリー&デプロイ)の一環に組み込んで自動実行したりと、機能も充実しています。
ただ、iOSアプリのエミュレータ上でのテストになるため実行速度が遅く(MagicPodというより、裏で動作しているAppiumの特性のようです)、また、自動化に向かないテスト項目もあります。
手動テストのコストをゼロにするのは、まだしばらく難しそうだなという印象です。

リグレッション(デグレ)は手戻りを発生させるだけでなく、ひとたび世に出てしまえばブランドイメージへの影響や、ユーザの離脱を招きます。
そうした起こるかもしれないリスクを未然に防ぐ品質への意識・風土を作っていくことも、こうした取り組みの重要な役割だと感じています。

参考: 記事中の写真は pexels を利用しています

*1:VRTでは、変更前後のスクリーンショットを比較して、画面上に意図しない変化が出ていないかを確認します