GMO DevelopersDay
GMO DevelopersDay
2024.11.29Fri - 11.30Sat YouTube Live
イベント開催まであと6

PHPerKaigi 2023 登壇レポート -Vol.02

登壇:GMOペパボ「Win Testing Trophy Easily / テスティングトロフィーを獲得する 」

2023年3月23日(木)〜25日(土)に技術カンファレンス「PHPerKaigi 2023」が練馬区立区民・産業プラザ Coconeriホールおよびオンラインにて開催されました。

GMOインターネットグループはダイヤモンドスポンサーとして協賛し、
登壇セッションでは、GMOペパボ株式会社より小山 健一郎が登壇しました。その内容をご紹介します。

はじめに

テスト戦略を策定する際の指針となるコンセプトの1つに「テストピラミッド」があります。テストピラミッドでは、ユニットテスト、インテグレーションテスト、E2Eテストの3つのレベルに分けて、この順でテストの量を小さくしていくピラミッド型にすることが推奨されます。一方で、React Testing Libraryの作者であるKent C. Dodds氏は「テスティングトロフィー」というインテグレーションテストに最も重点をおいたテストコンセプトを提唱しています。複数の選択肢があるなか、テスト構成の最適解はどのように考えていけばよいのでしょうか。2023年3月23日(水)〜25日(土)に練馬区立区民・産業プラザ Coconeriホールおよびオンラインにて開催された技術カンファレンス「PHPerKaigi 2023」で、GMOペパボ株式会社 小山 健一郎氏が自社での事例をもとに解説しました。

登壇者

テストピラミッドとテスティングトロフィーの違い

小山氏は、講演冒頭で単体テストの原則とそのパターンをまとめた書籍『単体テストの考え方/使い方』(マイナビ出版、2022年)を紹介しました。同書籍によると、テストピラミッドは、テスト・スイートにおいて、「単体テスト(ユニットテスト)」「統合テスト(インテグレーションテスト)」「E2E(エンドツーエンドテスト)」という異なる種類のテストがそれぞれ特定の割合でテストケースを持つようになっていることを提唱している概念とされています。そして、テストピラミッドの横幅は、テスト・スイートを構成する各種類のテストに含まれるテストケースの数を示します。そのため、テストピラミッドの層が横に長いものほど、テストケースが多いことになります。また、テストピラミッドの高さは、対象のテストがどれくらいエンドユーザーの視点に近いのかを示します。

一方で、テスティングトロフィーでは、テストの構成割合がテストピラミッドとは異なります。横幅をみると、インテグレーションテストが最も大きくなっており、重点的にインテグレーションテストを実施することが推奨されます。小山氏は「信頼性と速度・コストのトレードオフのバランスを上手くとっている、インテグレーションテストに労力を費やすと効率よくテストが進められるという主張だと考えられる」と説明します。

テストの分類

そもそも、ユニットテスト、インテグレーションテスト、E2Eテストには厳密な定義がなく、人によって認識がずれてしまうことも多くあります。ここで小山氏はそれぞれの考え方を次のように整理します。

「『単体テストの考え方/使い方』によると、ユニットテストは1単位の振る舞いを検証すること、実行時間が短いこと、他のテストケースから隔離された状態で実行されることという3つの性質を持つ。これら3つのうち1つでも損なっているものをインテグレーションテストに分類する。ただし、この定義でもすべてが明確になるわけではなく、“隔離された状態”についても解釈の異なる考え方がある」(小山氏)

ここで、小山氏はテストの実行速度と決定性に着目したテスト分類としてテストサイズを取り上げます。これは『Googleのソフトウェアエンジニアリング』(オライリージャパン、2021年)で紹介されているテスト分類です。書類を元に小山氏は、テストサイズの定義を下記のように紹介します。

  • スモールテスト:単一プロセス(シングルスレッド)内で実行されるもの。sleep、I/O、ブロックは禁止
  • ミディアムテスト:単一マシン内で実行されなければならない。ローカルホスト以外のシステムへのネットワーク呼び出しは禁止
  • ラージテスト:複数マシンにまたがるテスト

よりわかりやすくするためにテスト対象のアプリケーションを具体的に示したうえで考えると、テストサイズは図のようなイメージとなります

「良いテスト」とは?

『単体テストの考え方/使い方』では、以下の項目が良いテストを構成する4本の柱とされています。

  • 退行(regression)に対する保護
  • リファクタリングへの耐性
  • 迅速なフィードバック
  • 保守のしやすさ

ただし、1〜3は互いに排反する性質であり、すべてを成り立たせることはできません。こうした制約のなかでは、リファクタリングへの耐性と保守のしやすさを可能な限り備えたうえで、リファクタリングへの耐性と迅速なフィードバックの2つのあいだでバランスを調整するのが現実的な戦略となります。

ここで、テストサイズの観点からテストピラミッドのテスト戦略を捉え直してみます。退行に対する保護は、ラージ>ミディアム>スモールとなりますが、保守にかかるコストが増加するほか、迅速なフィードバックは得られません。したがって、保守コストと迅速なフィードバックを考えると、テストケースの数は、スモール>ミディアム>ラージという戦略になります。

一方、テスティングトロフィーをテスト戦略で捉え直すと、信頼性が重要視されるため、品質保証、つまり退行に対する保護を優先します。結果、ラージ>ミディアム>スモールの順でテストの信頼指数(品質保証への貢献度)は高くなりますが、ラージを重視すると迅速なフィードバックが得られません。そこで、バランスの良いミディアムテストに重点を置くという考え方になります。

小山氏は「テストピラミッドは、ほとんどのアプリケーションに効果を発揮する構成だが、それがすべてではない。良いテストを構成する4つの柱と、テスト対象の状況やフェーズ、開発体制などさまざまなパラメーターをみて判断していくことが重要」とします。

GMOペパボの事例に見る、テスティングトロフィー実践の課題と解決策

ここから小山氏はGMOペパボでの事例をもとに、テスト構成の方針の立て方について解説していきました。

今回対象となったのは、新規開発のAPIサーバです。扱うドメインはシンプルで、ドメインロジックもあまり複雑ではありませんが、データの取り扱いを慎重に行う点、また長期運用が想定されている点に気をつける必要がありました。動作環境はコンテナベースを想定しており、開発言語はGo、APIの定義にはOpenAPI Spec v3を採用しスキーマ駆動開発で進める方針となっていました。

小山氏によると、アプリケーションのテスト戦略を考えたのは開発初期だったといいます。当時は開発言語が決まったくらいの時期で、アプリケーションの実装が見えてこないことによる難しさがありました。

特に今回は、長期運用に耐えうることが要件に盛り込まれていた点がポイントでした。これを意識し、「まず外部とのインターフェイスであるAPIスキーマが重要。接点がある以上は、APIの更新がありつつも残るはず。データベースの寿命はアプリケーションよりも長いと考えられるため、データベースのスキーマも大事」(小山氏)と方針を決めていきました。小山氏は「一方で、アプリケーションのアーキテクチャが長期的に持続するかわからない、アーキテクチャの検討もできていない状況で、自分が今から設計するアプリケーションそのものが信用できない状態だった」と振り返ります。

ここで有効となるのが、テスティングトロフィーの考え方です。アプリケーションの外部からのテストに重点を置くことで、アプリケーションの実装の変化に影響されにくいテスト構成にするというアプローチを取ることができるためです。ただ、実現にあたっては大きな課題がありました。ミディアムテストのコストがスモールテストに比べて大きいという問題です。

まずは、テストの実行コストです。迅速なフィードバックの観点からすると、一般的にミディアムテストはスモールテストより実行時間が長くなるため、「並行実行の仕組みで緩和しつつも、ある意味許容する必要があった」と小山氏はいいます。また、テストの作成保守コストも課題です。「APIにHTTPリクエストを送信して、意図した結果や状態になっているかを確認する」というテストを書くこと自体に手間が掛かってしまうため、できる限り簡単に書ける仕組みを構築することが求められます。

この解決策としてのがrunnというツールです。
runnは、シナリオに沿って操作を自動化できるパッケージ/ツールで、HTTP、gRPC、データベースをはじめさまざまなプロトコルに対応しています。

シナリオファイル(YAML)のフォーマットは、OpenAPI Specライクであり、シナリオ内のステップ間の値が自動で連携されるので、「ステップAを実行してからその結果を使ってステップBを実行する」というシナリオを構築しやすいといえます。分割実行、サンプリング実行、ランダム実行、並行実行なども可能で、ループ実行やリトライ実行の仕組みも組み込みで備わっています。

runnでは、1つのシナリオは1つ以上のステップから構成されており、1シナリオは1YAMLファイルになっています。これをランブックと呼びます。シナリオごとにランナーと呼ぶステップ実行コンポーネントを定義し、ランナーを使って各ステップを実行していくというアーキテクチャになります。

「ログインしてプロジェクト一覧を取得する」というシナリオは以下のとおりです。ランナー、変数を設定して、各ステップを書いていくという流れになっていることがわかります。

このように、ミディアムテストの課題であったテストの作成保守コストは、runnでスモールテスト並みに削減することができました。

「テスト構成の最適解はテストピラミッド一択ではない」

テスティングトロフィーを実践してみて「あくまで主観だがうまくいった」と振り返る小山氏。具体的な効果について次のように紹介しました。「1つのシナリオあたりのコードカバレッジが非常に大きいので、リファクタリングへの耐性、退行に対する保護への効果が大きい。シナリオの拡充もYAMLを書くだけでよいためかなり捗る。また、シナリオがOpenAPIライクだったので、エンジニアにとって読みやすいものになり、オンボーディングにも役立つ」(小山氏)

今後の規模拡大やアーキテクチャの安定化などの要因によってテスト構成がピラミッドに近づいていくことは大いにありうるといいますが、小山氏は「現時点においては(テスティングトロフィーの採用は)悪くない判断だった」と評価します。そのうえで「テスト構成の最適解はテストピラミッド一択ではない。その都度、状況にあわせて考えていきたい」と語りました。

ブログの著者欄

技術広報チーム

GMOインターネットグループ株式会社

イベント活動やSNSを通じ、開発者向けにGMOインターネットグループの製品・サービス情報を発信中

採用情報

関連記事

KEYWORD

採用情報

SNS FOLLOW

GMOインターネットグループのSNSをフォローして最新情報をチェック