API負荷テストが実際に測定しているもの
処理能力: レイテンシが悪化し始めるまでに、毎秒どれだけのリクエストを処理できるか。同時アクセスのトラフィックを生成し、レスポンスタイムとエラー率を観測することで答えが得られます。
正しさ: レスポンスに正しい内容が含まれているか。レスポンスボディを期待値と照合してアサーションを行うことで答えが得られます。
ステータスコードしか確認しない負荷テストは、すべてのレスポンスが空のリストを返していても、問題なしと報告します。そのサービスは速くて間違っていたわけですが、速くて間違っている状態は、遅くて正しい状態よりも厄介です。誰も調査しようとしないからです。
機能テストスイートが負荷について教えてくれること、教えてくれないこと
生成されるAPIテストプランには、境界値やストレスに近い形のケースが含まれます。過大なペイロード、範囲外の値、通常とは異なるパラメータの組み合わせなどです。これらは実在する種類の障害を捕捉しますが、負荷テストではありません。継続的な同時アクセス下での振る舞いを測定するのではなく、エッジケースを1つずつ試しているからです。
そのため、率直に線を引くとこうなります。処理能力には専用の負荷テストツールを使います。正しさの検証には機能テストを使い、扱いにくい入力でしか現れないエッジケースもそこでカバーします。どちらも他方の代わりにはならず、両方を実施するほうが、本番環境でその隙間に気づくよりも安上がりです。
負荷テストを始める前に確認しておきたい3つのこと
毎秒1リクエストで正しく動作していますか
壊れたエンドポイントに負荷テストをかけても、どれだけ速く間違えられるかを測っているだけです。
後片付けは行われますか
レコードを作成したまま残していく負荷テストの実行は、次回の実行時の振る舞いを変えてしまいます。
その環境は本番環境を反映していますか
データ量が10分の1の環境から得た負荷テストの結果は、予測ではなく単なる数値です。
現実的な条件下での正しさ
埋めるべき隙間は、負荷テストと機能テストの間にあるのではありません。多くの機能テストスイートがクリーンでアイドル状態の環境に対して実行されるため、データ量が大きいときや状態が乱れているときに現れる障害を一度も目にしない、という点にこそあります。実際に使い込まれた環境に対して機能テストを実行することは、そうした障害を見つける安上がりな方法です。
ターミナル
npm install -g @testsprite/testsprite-cli
testsprite setup
ローカルに何もインストールしたくない場合は、TestSpriteのダッシュボードからも同じセットアップを行えます。CLIのその他の機能については、 CLIリポジトリをご覧ください。
APIテストスイートが長持ちするかどうかを決める4つの要素、すなわちセッション、取得した値、実行順序、クリーンアップについては、 APIテストのドキュメントで解説しています。
負荷テストの結果を正直に読む
負荷テストの結果は、他のどのテスト出力よりも過大に解釈されがちで、その方向は多くの場合2つあります。
本番環境と一致しない環境から得た数値は、次回の実行と比較するための基準点にすぎません。データベースが半分、データ量が10分の1、他のトラフィックがない、インスタンスサイズが違う、といった具合です。その数値を処理能力として提示することが、測定したはずの数値にリリース当日になって驚かされる原因になります。
もう一方の方向は、テストの成功を健全性の証拠として扱うことです。実行時にステータスコードしかアサーションしていなければ、測定できたのは「負荷の下でサービスが素早くレスポンスを返す」という事実だけです。そしてクエリが空の結果を返しているときほど、サービスはそれを非常に効率よく実現します。一部のリクエストに対してレスポンスボディのアサーションを有効にすれば、同じ実行から意味のある情報が得られます。
変更のたびに正しさを検証する
負荷テストは、定期実行またはリリース前に行うものです。正しさの検証はすべてのプルリクエストで行うべきです。その時点であれば、リグレッションの修正コストが最も低く済むからです。
GitHub App は、TestSpriteのダッシュボードで設定するWebhookです。パイプラインがすでに生成しているデプロイイベントを監視するため、リポジトリ側に変更は必要ありません。
GitHub Actions は、そのステップを自分のワークフローの中に組み込む方法で、設定はターミナルから行います。
負荷テストの周辺でTestSpriteが担う役割
TestSpriteは、処理能力の数値に意味を持たせる「正しさ」のレイヤーを担います。期待される振る舞いに照らしてAPIを実行し、境界値や認可のケースも対象に含めたうえで、どれだけ速かったかだけでなく、実際に何が返ってきたかを報告します。
テストは、稼働中のサービスに対する探索パスと、手元にある仕様をもとに生成されるため、扱いにくい入力も誰かが書かなくてもカバー範囲に含まれます。Auto-Authenticationは実行中のセッションを維持し、Dynamic Variablesは呼び出し間で値を引き渡し、Dependency Chainsは各ケースが必要とするものと生成するものから実行順序を導き、Auto-Cleanupはその実行で作成されたものだけを削除します。
これによって、パフォーマンス改善の作業に土台ができます。スループットの測定に1日を費やす前に、そのエンドポイントが正しい内容を返すとわかっているからです。継続的な同時アクセスは引き続き負荷生成ツールが担当し、両者は互いに干渉することなく、異なる頻度で実行されます。
TestSpriteは負荷テストを行いますか?
いいえ。TestSpriteが検証するのは、境界値ケースを含む正しさです。継続的な同時アクセス下での処理能力を測るには、専用の負荷テストツールが必要です。
負荷テストでレスポンスボディにアサーションをかけられますか?
ほとんどの負荷テストツールが対応していますが、有効にしているチームはわずかです。負荷生成側のスループットを消費するためです。すべてのリクエストではなく、一部のサンプルに対して実施する価値があります。
どちらを先に行うべきですか?
正しさの検証です。誤った内容を返すエンドポイントに負荷テストをかけても、何も語らない数値が自信たっぷりに出てくるだけです。
負荷テストはどのくらいの頻度で行うべきですか?
リリース前と、アーキテクチャの変更後です。継続的な負荷テストはコストが高く、その間の期間に新しい発見をもたらすことはほとんどありません。
CIでの負荷テストはどうですか?
スモークテスト程度の規模の負荷チェックであれば機能します。CIで本格的な処理能力テストを行うと、たいていは意味のない負荷レベルになるか、パイプラインが非常に遅くなるかのどちらかになります。