はじめに
E2EテストやUIテストだけで「問題なし」と判断したにも関わらず、リリース後にデータ不整合のバグが発覚した経験はないでしょうか。
実務において、以下のような「画面の見た目に騙される」ケースは少なくありません。
- 画面上は正常に表示されているが、DBの値が誤っている
- APIレスポンスとDBの格納内容が一致していない
- ユーザーの連続操作によって、裏側でデータの先祖返りが起きている
本記事では、こうした「見えないバグ」を防ぐための API × DB観点でのテスト戦略 について、実務ベースの事例を交えて解説します。
なぜUIテストだけでは不十分なのか
UI(画面)はあくまで「内部処理の結果の一部」を出力しているに過ぎず、システムの正しい状態を100%保証するものではありません。
| 事象 | 原因 |
|---|---|
| 画面上はエラーに見えない | フロントエンド側のフォールバック処理がAPIの失敗を隠蔽している |
| 画面に「成功」と表示される | DBへの保存処理が失敗・ロールバックされている |
| リロードで古いデータが表示される | 非同期処理の影響で画面が古い状態に上書きされる |
「見た目が正しい = データが正しい」ではない という前提に立つことが、QAエンジニアにとって非常に重要です。
実務で遭遇したバグ事例
ケース:価格履歴の取得ロジック不具合(不動産システム)
不動産系システムにおいて、物件の価格履歴テーブルから「最初の価格(基準価格)」を特定して画面に表示するロジックに不具合がありました。
🚨 発生していた事象
- 同一の物件に対して、複数の価格変更履歴が存在する
- データの移行(マイグレーション)や過去の仕様変更の影響で、変更日(
changed_date)がNULLのデータが混在している - システムの自動一括更新により、作成日時(
created_at)が完全に同一のレコードが複数存在する
この状況下で、従来のテスト(正常なデータパターンのみの画面確認)では気づけなかったバグが発生しました。
❌ 原因となった不十分なSQLロジック
開発環境のソースコード(裏側のクエリ)を確認すると、以下のような単純なソートになっていました。
ORDER BY changed_date ASC
この条件では、以下の問題が発生します。
-
NULLの扱い(先頭に来るか、末尾に来るか)が RDBMS の仕様や設定依存になり不明確 - 日時が完全に同一であるレコードの優先順位が不定(ランダム)になる
結果として、「画面を開くたびに、または環境によって、表示される歴史的価格が変わる」 という致命的なデータ不整合(flakyなバグ)を引き起こしていました。
✅ 改善アプローチ(QA視点でのロジック指摘)
仕様を明確化し、以下のようにソート順の優先順位を厳密に定義(Window関数の導入など)するよう修正を依頼しました。
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY trn_property_id
ORDER BY
changed_date IS NULL ASC, -- NULLを明確に末尾(または先頭)に制御
changed_date ASC, -- 日付順を明確化
created_at ASC -- 同一日時の場合はIDや作成順で一意に決定
) AS rn
FROM trn_property_prices;
修正のポイント
-
NULLの評価順を明確に制御した - 日付が同じ場合のセカンドキー(
created_at)を指定し、取得結果の決定論的(Deterministic)な一意性を担保した
API × DB テスト戦略:実務で使える5つのチェックポイント
裏側のデータまで品質を担保するために、QAチームが実践すべきアクションです。
1. APIレスポンスとDBを必ず「突き合わせる」
画面のテキストを見るだけでなく、開発者ツール(Networkタブ)でAPIの生のJSONレスポンスを確認し、それが実際のDBレコードと1対1で整合しているかをSQL(LEFT JOIN などを用いた不整合チェッククエリ)で直接検証します。
-- 例:APIが返すIDに対応するDBレコードが存在するかチェック
SELECT
api.id,
db.id AS db_id,
CASE WHEN db.id IS NULL THEN '❌ 不整合' ELSE '✅ 一致' END AS status
FROM api_response_ids api
LEFT JOIN actual_table db ON api.id = db.id;
2. ステータスコードだけで「正常」と判断しない
APIが 200 OK や {"status": "success"} を返していても、中身のデータが欠損していたり、型(String型とInt型の取り違えなど)が誤っているケースがあります。レスポンスボディのスキーマ検証まで踏み込みます。
200 OK ≠ データが正しい。レスポンスボディのスキーマと値まで必ず検証する。
3. 境界条件・異常データ(境界値)を直接インプットする
UIの入力バリデーションをバイパスして、APIツール(Postmanなど)やDB操作を通じて以下のデータを直接投入し、システムの挙動を確認します。
| 投入パターン | 確認観点 |
|---|---|
NULL / 空文字 ""
|
デフォルト値処理・エラーハンドリング |
| 特殊文字・全角半角混在 | エスケープ処理・文字化け |
| 重複するユニークキー | 制約エラーの適切な返却 |
4. テスターもSQLを必須スキルとする
データの裏付けを取るために、以下のSQL操作はQAの日常業務に組み込むべきです。
-- 更新前後の状態差分確認
SELECT * FROM target_table WHERE id = :target_id;
-- 親子テーブル間の孤立データ検出
SELECT child.*
FROM child_table child
LEFT JOIN parent_table parent ON child.parent_id = parent.id
WHERE parent.id IS NULL;
-- 意図しない重複データのあぶり出し
SELECT column_a, COUNT(*)
FROM target_table
GROUP BY column_a
HAVING COUNT(*) > 1;
5. 非同期処理・イベント競合を考慮する
「APIを叩いた直後に画面遷移する」といったシナリオでは、バックエンドのDB書き込み処理が完了する前に次の画面の読み込み(SELECT)が走り、古いデータが表示されることがあります。時間差(レイテンシー)を意識したテスト設計が必要です。
APIコール → [非同期書き込み中] → 画面遷移 → SELECT(古いデータが返る!)
↑
ここの時間差を見落とさない
よくあるアンチパターン
❌ UIだけでOKと判断する
バックエンドのデータ構造が崩れていることに気づかず、将来的なデータ移行やバッチ処理で大事故を起こす。
❌ 正常系データしか見ない
本番環境でユーザーがイレギュラーな操作(ブラウザバック、連打など)をした際に、データの先祖返りや重複が発生する。
❌ 「仕様書に書いていないから」とキーイベント操作を無視する
ダイアログ表示中に背後で Delete キーが反応してデータが消えるような、致命的なUX・セキュリティ不備を見落とす。
まとめ
API × DBの観点を取り入れたテストの本質は、以下の通りです。
| # | 本質 |
|---|---|
| 1 | UIだけに依存せず、データの真実(Single Source of Truth)を見る |
| 2 | SQLを活用して、ブラックボックステストをグレーボックス化する |
| 3 | 異常系や環境依存の境界値を重視する |
QAエンジニアの最大の価値は、「目に見える画面の動作確認」ではなく、「見えないところにあるリスク(バグ)を未然に見つけること」 にあります。
裏側のデータ構造にまで興味を持つことで、テストのクオリティは劇的に向上します。
おわりに
テストは「仕様を疑うこと」から始まりますが、APIやDBを含めた検証は 「仕様の裏にある『事実』を確認すること」 に近いと考えています。
表面的な挙動に満足せず、一歩踏み込んだデータ検証を行うことで、プロダクトの信頼性は確実に変わります。
今後も、日本の不動産IT(PropTech)などの実務現場で得た知見をもとに、一歩進んだテスト戦略について発信していきます。
最後までお読みいただき、ありがとうございました!