| name | nonfunctional-perf |
| description | test-catalog の手法カタログの一部。非機能テストの測定系(性能テスト、負荷テスト、 ストレステスト、スパイクテスト、ソークテスト/耐久テスト、スケーラビリティテスト、 キャパシティテスト)を検証したいときに使う。通常は test-catalog スキルの索引経由で 手法が選定された後にこのスキルを直接参照する。
|
| disable-model-invocation | true |
非機能テスト(測定系: 性能・負荷・容量)
非機能のうち「どれだけ速く・どれだけ捌けるか」を数値で測る観点を集める。
性能、負荷、ストレス、スパイク、ソーク(耐久)、スケーラビリティ、キャパシティの7種。
共通の型は「測定指標 + 合否しきい値(SLO/SLA)を先に決め、超えたら CI を落とす」ことだ。
しきい値の無い計測は「走らせただけ」で終わり、回帰を守らない。
品質保証系の非機能(セキュリティ、a11y、互換、信頼性、可用性、カオス、耐障害性、コントラクト、データ品質)は nonfunctional-attributes.md に分けた。
開発プロセスに組み込む戦術(BDD/ATDD/CI/カナリア)、静的テスト、その他の観点(冪等性/並行性)は process-static.md。
目次
測定系の非機能テスト(7種の一覧)
ISO/IEC 25010 の性能効率性に対応する。負荷の軸(時間 × 量 × 変動)を取りこぼさないために種別を分ける。
- 性能テスト(Performance):応答時間、スループット、リソース使用量を測る。基準値を満たすか確認するのが目的。
- 負荷テスト(Load):想定される通常からピークまでの負荷をかけ、その範囲で性能が劣化しないことを確認する。
- ストレステスト(Stress):想定を超える負荷をかけ、破綻する限界点と破綻の仕方(graceful degradation か全停止か)を見る。
- スパイクテスト(Spike):負荷を瞬間的に急増させ、急な変動への追従と回復を確認する。
- 耐久ソークテスト(Soak):通常負荷を長時間かけ続け、メモリリークやリソース枯渇など時間で蓄積する劣化を検出する。
- スケーラビリティテスト:負荷やデータ量の増加に対し、リソース追加で性能が伸びるか(水平か垂直か)を確認する。
- キャパシティテスト:要件を満たせる最大の同時利用者数やデータ量を見極め、増設計画の根拠を得る。
この7種は「広く薄い一覧」で足りる。深掘りが要るのは実際に手を動かす負荷テストで、以下に遂行手順と完了チェックを置く。
他種も同じ骨格(SLO を決める → 該当する負荷パターンをかける → 指標を計測 → しきい値と突合)で、負荷パターンだけが変わる。
遂行手順: 負荷テスト(着手→完了)
負荷テストの本体は「先に合否ラインを決め、再現可能な負荷をかけ、指標としきい値を突合する」ことだ。順序を守らないと「走らせて数値を眺めただけ」になる。
- 目標 SLO を決める:合否ラインを数値で先に書く。例「p99 < 200ms、エラー率 < 0.1%、スループット ≥ 500 RPS」。SLO が無いまま計測すると合否を後付けで甘くできてしまう。
- 負荷シナリオを定義する:対象エンドポイントと、同時接続数(connections)または目標 RPS、継続時間(duration)を決める。通常時とピーク時の2点を最低限おさえる。本番に近いリクエスト分布(read/write 比、ペイロードサイズ)に寄せる。
- ランプアップを入れる:いきなり最大負荷をかけず、接続数を段階的に上げる。立ち上げ直後のコールドスタートや接続プール枯渇を、定常状態の劣化と切り分けるため。
- 指標を計測する:p50/p95/p99 レイテンシ、スループット(RPS)、エラー率の4点を取る。平均値だけ見ない(平均は裾の悪化を隠す。SLO は必ず p99 等のパーセンタイルで判定する)。
- SLO と突合して合否を返す:計測値を手順1のしきい値と比較し、1つでも超えたらテストを失敗にする(
throw で CI を落とす)。ここを実装しないと回帰が守られない。
- 負荷源と対象を分離する:負荷生成プロセスと被測定アプリを別プロセス(できれば別ホスト)に置く。同居させると負荷源の CPU 消費が測定値を汚す。
完了チェック: 負荷テスト(もれ確認)
- しきい値が実装されているか:計測結果を SLO と比較して
throw/失敗させる行が現にあるか。「結果を出力するだけ」で合否判定が無ければ未完(走らせただけ)。
- パーセンタイルで判定しているか:合否が平均値でなく p95/p99 で判定されているか。平均だけだと裾の悪化(一部ユーザーの遅延)を見逃す。
- エラー率を見ているか:レイテンシだけでなくエラー数/率もしきい値に入っているか。速いが失敗だらけ、を緑にしない。
- 負荷源と対象が分離されているか:負荷生成が被測定プロセスと同居していないか(同居なら数値が信用できない)。
- 時間・変動の軸を別種でカバーしたか:long-running の劣化(soak)、急変(spike)、限界(stress)を、この負荷テスト1本で代用していないか。それぞれ別シナリオが要る。
TypeScript example: 負荷テスト
依存を増やさず Node だけで済むので autocannon をスクリプトから呼ぶ。
p99 レイテンシとエラー数を基準にして、超えたら失敗させる。
import autocannon from "autocannon";
const result = await autocannon({
url: "http://localhost:3000/api/health",
connections: 50,
duration: 10,
});
if (result.errors > 0) throw new Error(`errors: ${result.errors}`);
if (result.latency.p99 > 200) throw new Error(`p99 too high: ${result.latency.p99}ms`);
性能改修の順序(計測→特定→修正→オラクル併走)
「遅い」報告を受けて直すときは、テストと同じく合否を数値で確定させてから手を動かす。
- プロファイル/計測で犯人を特定してから直す。それらしい原因を推測で直すのは、当たっても外れても1回分の修正・再デプロイ・再計測を消費する。数値の符合(「体感の遅さ ≒ 既知のタイムアウト値の累積」等)は状況証拠であって原因ではない。症状(待たされた側の挙動)と原因(待たせた側の処理)を混同しない。
- 区間を段階的に絞る。境界ごとのタイムスタンプ差分で「どの2点の間で時間が消えるか」を1回の実行で特定し、その区間の候補処理を小さなマイクロベンチマークで個別計測して犯人を1つに確定する。関数を1つずつ疑って再実行を繰り返すより速い。
- 遅い版を消さずに差分テストのオラクルとして残す。最適化は結果が一致してこそ意味がある。素朴で明らかに正しい旧実装を参照に、ランダム+境界入力で出力一致を確認する(
oracle-differential)。参照が高コストなら全件でなく抜き取りで比較する(オラクル自体がテストをタイムアウトさせない)。
- 支配項を潰したら次の項は実測してから着手する。残りの改善が SLO・体感に現れるかを測ってから決める(YAGNI)。
測定系の網羅の定義
非機能は「網羅率」でなく「品質特性 × 基準値」で網羅を定義する。
網羅基準: ISO/IEC 25010 の性能効率性に関係する特性ごとに「測定指標 + 合否しきい値(SLO/SLA)」を定め、対象特性をすべて1つ以上のしきい値テストで覆ったとき網羅とみなす。
性能なら p99 とスループット、容量ならキャパシティ上限を指標にする。
網羅手順:
- 対象に関係する性能特性を 25010 から選ぶ。
- 各特性に測定指標としきい値を決める。
- 各しきい値を超えたら CI を落とすテストにする(「走らせただけ」で終わらせない)。
達成チェック: しきい値の無い計測(走らせただけ)が残っていないか、長時間蓄積(soak)、急変(spike)、限界(stress)など時間と負荷の軸を取りこぼしていないか。
品質保証系(セキュリティ、a11y、信頼性、耐障害性、コントラクト、データ品質)の網羅の定義は nonfunctional-attributes.md の末尾にある。
セキュリティの検出層分け、a11y の WCAG 違反0件など、測定指標が数値レイテンシと異なる観点はそちらを参照する。