本記事は『Web APIテスト技法』の「第1章 Web APIをテストする理由と方法」から一部を抜粋したものです。掲載にあたって編集しています。
なお、本書で使用しているサンプルアプリケーションはこちらで公開されています。
Web APIをテストする理由と方法
自分たちが構築しているシステムが高品質でエンドユーザーのために役立つものになることを保証するためにはどうすればよいだろうか。品質の高いプロダクトを提供するために直面する課題とは、この仕事で発生する複雑な工程、作業が膨大な数に上ることだ。品質向上のために確かな知識に基づいて適切な行動を選択したいなら、この複雑さを克服し、システムがどのような仕組みで動作し、ユーザーがシステムに何を望んでいるかについての理解を深める必要がある。
自分たちが実際にどのようなものを作っているかをよりよく理解するために、しっかりとしたテスト戦略を取り入れなければならないのはそのためだ。そこで、APIテストの長い道のりに踏み込む前に、ソフトウェアがどうして複雑になるのか、その複雑さに対処するためにテストがどのように役立つかについて考えておこう。
Web APIのなかで何が起きているか
イギリス政府は、HMRC(Her Majesty’s Revenue and Customs:歳入関税庁)を含む政府各部門で「Digital by Default Service Standard(デジタルをデフォルトのサービススタンダードに)」というデジタル戦略を2013年に立ち上げた。HMRCの目標は、サービス向上と経費削減のためにイギリスのすべての納税業務をオンライン化することだった。
HMRCは、2017年までに、5つのデリバリーセンターの60の開発チームが作った100種以上のデジタルサービスを抱えるようになった。これらのデジタルサービスは、絶えず成長し(成長は今も続いている)、相互に接続し合うWeb API群で構成されるプラットフォームがサポートしていた。これらのデジタルサービスをサポートするために作られたAPIの数は目も眩むほどの数に上る。そして、その後もAPIの数は間違いなく増えている。
私が2015年にプロジェクトに参加したときには、サービス、チーム、デリバリーセンターとも今の半分ほどだったが、それでもプラットフォームには100種を越えるWeb APIがあった。Web APIの数は間違いなくもっと増えているはずだ。すると、当然湧いてくる疑問は、これだけの規模と複雑さを持つプロジェクトでエンドユーザーに高品質のサービスを提供するためにはどうすればよいのかである。
私がHMRCのプロジェクトを取り上げたのは、Web APIを構築するときにいつも直面する2つの複雑さの「レベル」を強調するために役立つからだ。
- Web API内部の複雑さ
- 1つのプラットフォームで多数のWeb APIが共同で仕事を進める複雑さ
これら2種類の複雑さの理解を深めると、テストがなぜ必要なのか、テストがどのように役立つのかがわかってくる。
Web API内部の複雑さ
Web APIとは何なのかという問いから話を始めていくのは単純すぎるように思われるだろうか。しかし、時間を割いてWeb APIの構造について考えると、Web APIとは何かだけではなく、その複雑さがどこから来ているのかがわかる。たとえば、本書でこれからテストする宿泊予約Web APIの構成を図にすると、図1のようになる。
この図を見ると、このWeb APIがクライアントからのHTTPリクエストという形で予約情報を受け取り、それをきっかけとしてAPI内のさまざまなレイヤ(層)の実行が開始されることがわかる。そして実行が完了して予約情報が保存されると、Web APIはHTTPを介してレスポンスを返す。しかし、このWeb APIの動作をもっと細かく見ていくと、1つのWeb APIのなかでどれだけのことが行われているかがはっきりと見えてくる。
まず、プレゼンテーション層が予約のHTTPリクエストを受け取り、それをほかの層が読み出せる形に変換する。次に、サービス層が予約情報を受け取り、ビジネスロジックを適用する(たとえば、予約情報が有効なものか、ほかの予約情報と矛盾していないかのチェックなど)。最後に、与えられた予約情報が保存を必要とするものなら、永続化層がストレージを確保し、データベースに格納する。以上がすべて成功したら、各層が呼び出し元の層に応答し、Web APIがリクエストを送ってきたコンポーネントに送るレスポンスを組み立てなければならない。
これらのレイヤは、要件と開発者の好みによって異なる形で構築できる。たとえば、Web APIは、REST、GraphQL、SOAPなどのアーキテクチャからどれを選ぶかを選択できる。そしてこれらのアプローチはそれぞれのパターンとルールを持っており、私たちはそれを理解しなければならない。
サービス層にはビジネスロジックも含まれている。ビジネスロジックは、現場のコンテキストによって異なるさまざまなカスタムルールを規定する。永続化層にも同じようなことが当てはまる。これらの各層は、それぞれ異なる開発ライフサイクルを持つ依存コードも使っている。高品質のシステムを提供するためには、膨大な量の情報を意識しなければならない。
Web APIのなかで何が行われ、それらが互いにどのように助け合っているかを理解するためには、時間と専門的な技能が必要になる。確かに、部品を個別にテストすればある程度の理解は得られる(私はこの作業を推奨している。詳しくは、J・B・レインズバーガーの講演「統合テストは詐欺だ」を見ていただきたい)が、そこでわかるのはパズルのピースだけであり、パズル全体ではない。
多数のWeb APIが絡み合う複雑さ
先ほど触れた100個以上のWeb APIで構成されるHMRCプラットフォームについて考えてみよう。個々のWeb APIがどのように動作し、それらが互いにどのように関わり合っているかについての理解は、どうすれば維持できるのだろうか。マイクロサービスアーキテクチャのようなアプローチを取れば、個々のWeb APIを小さく専門分化していくことによって1つ1つのWeb APIのなかの複雑さは軽減される。
しかし、その一方で、プラットフォームに追加されるWeb APIの数は増える。Web APIを集めたプラットフォームについての知識を最新状態に保つためにはどうすればよいだろうか。そして、個々のAPIがほかのAPIとどのようにやり取りするかを管理し、それらのつながりが想定されたパラメータの範囲内に収まっていることを確認するためにはどうすればよいのか。
品質の高いプロダクトを作るためには、確かな知識に基づいて行動を選択をしていかなければならない。その知識とは、自分たちのWeb APIがどのように動作し、Web API相互がどのように関わり合っているかについての知識である。ここできわめて重要な役割を果たすのがエンドユーザーだ。確かな知識に基づく選択ができていなければ、知識不足のためにシステムがどのように動作すべきかを誤解し、プロダクトが問題を起こすリスクが生まれる。システムの動作についての理解を確立し、維持するために役立つという意味でテストの価値を実感するのは、このような見方に立ったときだ。
テストはどのように役立つか
テストを通じてチームが成功をつかむためには、テストの目的と価値についての理解を共有しなければならない。残念ながら、テストとは何で、何のために役立つかについてはさまざまな誤解がある。この点についての理解を深めていただくために私が使っているテストのモデルをご紹介したい(図2参照)。
このモデルは、ジェームズ・リンゼーが「あらゆる戦略で探究が欠かせない理由」のなかで示した図をベースにしたもので、2つの円から構成されている。左の円はイマジネーション(想像)、すなわちプロダクトに求めるものを表し、右の円はインプリメンテーション(実装)、すなわちプロダクトに実際に含まれているものを表す。テストの目的は、テストアクティビティを通じてこれらの円に何が含まれるかを最大限まで学ぶことである。2つの円のなかでテストをすればするほど、多くのことが学べ、次のことを達成できる。
- 品質に影響をおよぼすかもしれない潜在イシューの発見
- 2つの情報円の重ね合わせ。つまり、作っているものがどのようなものかを理解し、構築したいプロダクト、サービスになっているという自信を得ること
ここのところをもっと深く知るために、高品質の検索機能を提供しようとしている架空のチームの例を取り上げてみよう。
イマジネーション
イマジネーションの円は、プロダクトに何を望むかを表しており、その期待、要件には明示的なものと暗黙のものの両方が含まれる。この円では、テストは明示、暗黙の両方の要件をできる限り多く学ぶことに力を注ぐ。この作業では、書面に書かれたり口頭で合意されたりした要件を学ぶだけではなく、細部を掘り下げ、用語や考え方に含まれる曖昧さを取り除いていく。たとえば、プロダクトオーナーのようなビジネスやユーザーの代表が「検索結果は重要なものから並べてほしい」と自分の要件を言ったとする。
ここで明示的に言及された情報は、プロダクトオーナーが、検索結果を示すこととそれを重要なものから並べることを望んでいるということだ。しかし、求められたことの背後にある考え方やコンセプトをテストしていくと、暗黙の情報が無数に明らかになっていく。それは、次のような問いのリストという形を取るだろう。
- 重要な結果とはどういう意味か?
- 誰にとって重要なのか?
- どのような情報を示すのか?
- 重要度による並べ替えをどのように行うか?
- どのデータを使うべきか?
これらの問いに答えていくと、何が望まれているのかがより明確になり、チームの考えに含まれている誤解が取り除かれ、これらの要件に影響をおよぼす潜在的なリスクが明らかになる。自分たちが作ることを期待されているものが何かをより深く理解すれば、最初から正しいものを作れる可能性が高くなる。
インプリメンテーション
イマジネーションをテストすると、作ってほしいと思われているものは何かがよりはっきりとわかる。しかし、何を作るべきかがわかっているからと言って、その要件を満たすプロダクトができるわけではない。そこで、次のことを学ぶためにインプリメンテーションもテストしなければならない。
- プロダクトが期待、要件を満たすものになっているかどうか
- プロダクトがどのような点で期待外れになっているか
この2つの目標は同じように重要である。適切なものを作ったことを確認したいのは当然だが、プロダクトの意図せぬ動作、脆弱性、要件に合わない部分、明らかに変な部分といった検討すべき副次的な問題は必ずある。私たちの検索機能の例の場合、検索結果を重要なものから並べて表示することをテストするだけではなく、次のようなことも確認すべきだ。
- 異なる検索語を入力するとどうなるか?
- 重要なもの順の結果とほかの検索ツールのふるまいに整合性がなければどうなるか?
- 検索中にサービスの一部が落ちていたらどうなるか?
- 5秒以内に1,000回検索を要求したらどうなるか?
- 検索結果がないときにはどうなるか?
要件を超えたところまで探っていけば、プロダクトのなかで何が行われているか、どのような癖があるかといったこともはっきりしてくる。そうすれば、プロダクトのふるまいについて間違った思い込みを持ったり、低品質のプロダクトをリリースしたりすることを避けられる。要件から外れたふるまいを見つけたときでも、要件を取り除いたり修正したりするという選択肢を持てる。
テストの価値
イマジネーションとインプリメンテーションのテストというモデルを知ると、テストが要件合致の単純な確認に留まらず、自分たちの思い込みを洗い出すという働きを持っていることがわかる。作りたいものと作ったもののテストについて学べば学ぶほど、この2つの円が重なり合う部分は多くなる。そして重なり合う部分が多くなればなるほど、自分たちの品質に対する尺度の正確性は増す。
驚くなかれ、あなたはすでにテストをしている!
テストの目的は、プロダクトに何を望み、プロダクトがどのように動作すべきかを理解し、学ぶことなので、あなたはおそらくすでに何らかのテストをしているということには触れておくべきだろう。コードのデバッグであれ、APIをロードして何かのリクエストを送ってみることであれ、API にどのような動作を求めているのかについてクライアントに質問することであれ、何をしてもあなたは学んでいる。だから、テストしているのである。
これは、テストが簡単な仕事のように思われる場合があることの理由でもある。しかし、思いつきの大雑把なテストと意識的で緻密に練り上げられたテストの間には違いがある。私たちは、プロダクトの複雑性が自分たちを圧倒するレベルになる場合があることを知っており、イマジネーションとインプリメンテーションの違いを見つけられるのはテストに対して戦略的なアプローチを取ったときだけである。
自分たちの仕事について確かな知識を持っているチームは、そうでないチームよりもプロダクトの品質とは何かをよく理解している。そういうチームは、品質向上のためにどのように作業を進めていくべきかの判断材料も豊富であり、特定のリスクに集中的に取り組んだり、ユーザーの要望により近づくための変更に踏み込んだり、修正のために時間を割くべきイシューと放置するイシューの間の線引きをしたりすることができる。優れたテストの価値はここにある。テストは、確かな情報に基づいて判断を下すことができ、品質の高いプロダクトを開発するための手順に自信を持てるという状態にチームを押し上げる力になるのだ。
APIテストの戦略的な進め方
イマジネーションとインプリメンテーションのモデルは、テストの目的と価値を説明する優れた方法だと思うが、少し抽象的な感じもある。このモデルは、APIのテストにどのように応用できるのだろうか。このアプローチを使ったAPIテスト戦略はどのようなものになるのだろうか。本書の目標の1つは、まさにそれを示すことだ。このモデルの理解を深めるために、あるAPIテスト戦略の例を見てみよう。これは、私が参加したHMRCプロジェクト以外のプロジェクトにも使えるはずだ。
このプロジェクトは、規程文書を検索、閲覧するとともに、その規定文書に従って報告書を作れるようにするサービスを開発するものだった。システムのアーキテクチャの概要は、図3のように描くことができる。
正確を期するために言っておくと、これは私が手掛けたアプリケーションの機能縮小版である。しかし、APIテスト戦略の策定を求められたときに扱うことになるタイプのアプリケーションはどのようなものかを示すことはできているはずだ。このモデルについては本書でさらに深く説明するが、ここで示しているだけでも、このアプリケーションが一連のWeb APIから構成されており、UIやほかのWeb APIにサービスを提供していることがわかる。
たとえば、Search APIはUIからのクエリを受け付けるが、Report APIなどのほかのAPIからのクエリも受け付ける。これでサンプルアプリケーションは手に入ったが、私たちが学んだテスト戦略モデルをこのアプリケーションのコンテキストに応用するためにはどうすればよいのだろうか。ここでも、図4のようなモデルでビジュアルに説明するのがもっともわかりやすいだろう。
図では、イマジネーションとインプリメンテーションの両方に、Web APIについて学ぶために役立つテストアクティビティが含まれている。イマジネーションの側には、次のようなテストアクティビティが含まれている。
- API設計テスト―――アイデアに疑問を投げかけ、自分たちが解決しようとしている問題が何かについての共通理解を築く。
- コントラクトテスト―――各チームのWeb APIが互いにやり取りでき、変更が起きたときに正しく更新されるようにする。
インプリメンテーションの側には、次のようなテストアクティビティが含まれている。
- 探索テスト―――Web APIがどのようにふるまうかを学び、潜在イシューを見つけられるようにする。
- パフォーマンステスト―――負荷がかかったときのWeb API のふるまいを理解するために役立つ。
そして、作りたいもの(イマジネーション)と作ったもの(インプリメンテーション)の知識が重なり合う領域に自動化されたAPIチェックがある。自動APIチェックは、APIの動作についての自分たちの知識がまだ正しいかどうかを確かめるとともに、潜在的な品質低下への注意喚起の役割を果たす。
これらのテストアクティビティについては、その他のテストアクティビティとともに、本書全体でもっと詳しく学んでいく。しかし、このモデルが異なるテストアクティビティでシステムの異なる側面に光を当て、異なる情報を明らかにしていくことはわかっていただけたはずだ。このモデルは、成功を収めているAPI戦略がホーリスティック(総合的、多面的)なアプローチを取り、さまざまなアクティビティを組み合わせて、自分たちとチームが確かな情報を把握し続けられるようにしていることを示している。このような戦略を生み出すためには、次のことをする必要がある。
現場のコンテキストとリスクを理解する―――ユーザーは誰か。ユーザーは何を望んでいるか。自分たちのプロダクトはどのように動作するか。自分たちはどのように仕事をしているか。それらにとって品質はどのような意味を持つか。 使えるテストアクティビティのタイプを理解する―――オートメーションを効果的に使う方法がわかっているか。コーディングを始める前にアイデアとAPI設計をテストできることを意識しているか。どうすれば、本番環境でのテストから意味のある情報を手に入れられるか。 現場のコンテキストについての知識を使って適切なテストアクティビティを選ぶ―――自分たちにとってもっとも大きな意味を持つリスクは何か。そのリスクを緩和するためにどのようなテストアクティビティを使うべきか。
本書は、これら3つのポイントを掘り下げ、あなたとあなたのチーム、会社や組織のために役立つテスト戦略を見極め、実現するために必要なスキルと知識をあなたにお届けする。これからの部分では、どのテストアクティビティがどこでもっともうまく機能するかを理解するためにこのテスト戦略モデルを使い、続いて自分たちのために役立つテスト戦略を確立していく。APIテストのさまざまなポイントに深入りする前に、自分たちのWeb APIプラットフォームを短時間で学ぶために役立つ少数のアプローチに慣れ親しむようにしていきたい。
まとめ
- Web APIにはいくつかのレイヤ(層)に分かれている。個々のレイヤは単独でも複雑な仕事をこなしており、レイヤが結合されると仕事はさらに複雑化する。
- 複数のWeb APIを組み合わせてプラットフォームのエンドユーザーが使うサービスを組み立てるときには、複雑さの規模はさらに上がる。
- 品質の高いプロダクトを提供するためには、この複雑さを克服し、理解することが大切になる。
- しっかりとした理解を得るためには、的確なテスト戦略が必要である。
- テストは、イマジネーションとインプリメンテーションの2つの領域を重点的に学ぶものだと考えられる。
- 作りたいものについてより深く学ぶためにイマジネーションをテストし、作ったものについてより深く学ぶためにインプリメンテーションをテストする。
- イマジネーションとインプリメンテーションの両方の領域についての知識が充実すればするほど、2つの領域は重なり合い、プロダクトの品質についてより確かな情報をつかめるようになる。
- 本書のテスト戦略モデルは、異なるテストアクティビティがイマジネーションとインプリメンテーションの2領域でどのような役割を果たすかを理解するために使える。
- 成功を収めているテスト戦略は、全体が渾然一体となってチームをサポートするようなさまざまなテストアクティビティから構成される。