🧑‍💻

#163 UIからAPIモックレスポンスをリアルタイム切り替え

に公開

はじめに 〜なぜUIからモックを切り替えるのか〜

フロントエンド開発では「APIモック」が欠かせません。しかし、従来のモックでは下記のような課題に直面することがあります。

  • 異常系の再現に手間がかかる
  • モックの状態変更にリビルドが必要
  • テストごとに異なる挙動を切り替えたいが面倒
  • 非エンジニアが自分で異常系を試せない

本記事では、UIからAPIモックレスポンスをリアルタイムで切り替える手法を紹介します。この仕組みにより、エンジニア・QA・デザイナー問わず、誰でも異常系挙動を即座に再現・検証できるようになります。

APIモックとMSWの基礎知識

なぜAPIモックが必要か?

  • バックエンド未完成でもUI開発を進めたい
  • エラー系や異常系を検証したい
  • E2EテストやCI/CDでも異常系の動作を確認したい

従来のAPIモックの課題

  • モック切り替えにコード編集+ビルドが必要
  • 状態の永続性がなく、毎回手動設定
  • QAエンジニアやPdMなど非開発者が自由に操作できない
    • ⇒バックエンドの開発が終わっていない場合や本番環境APIの利用に課金や制限がある場合、安定したテストを実施するためにQAでもモックAPIが使われることがあります。

MSW(Mock Service Worker)とは

  • Service Workerを活用し、実際のネットワーク通信を横取りしてモックレスポンスを返す
  • REST/GraphQL両対応、Node.js/Jest環境でも同じハンドラが使える
  • 本番に近い形でのUI/UX検証・自動テストが可能
  • 公式ドキュメント

UIからAPIモックを切り替える設計思想

1. 状態管理

  • localStorageやグローバル変数に状態を保存
  • リロード後もエラー状態が保持される
  • SSRやNode.js環境ではメモリやファイルで代用可能

2. MSWハンドラとの連携

  • 現在のエラー状態に応じてMSWハンドラのレスポンスを分岐
  • 異常系だけでなく、ネットワーク切断やタイムアウトも再現

3. UI/UX設計

  • 誰でも使えるモック切り替えパネル
  • 操作ミス防止のためのクリアボタン・確認導線
  • 状態がリアルタイムで反映される設計

4. セキュリティ・運用上の工夫

  • 本番ではUIパネル・モック動作を完全無効化
  • window直下の状態のみ操作可能にして不正アクセスを防止

実装サンプル

1. モック状態管理クラス

mockErrorState.ts
export type MockErrorType = number | 'network' | 'timeout' | null;

class MockErrorStateManager {
  private state: Record<string, MockErrorType> = {};

  setError(endpoint: string, error: MockErrorType) {
    this.state[endpoint] = error;
    localStorage.setItem('MOCK_ERROR_STATE', JSON.stringify(this.state));
  }
  getError(endpoint: string): MockErrorType {
    return this.state[endpoint] || null;
  }
  clearError(endpoint: string) {
    delete this.state[endpoint];
    localStorage.setItem('MOCK_ERROR_STATE', JSON.stringify(this.state));
  }
  getAllErrors() {
    return { ...this.state };
  }
}
export const mockErrorState = new MockErrorStateManager();

2. MSWのハンドラで分岐制御

handler.ts
import { rest } from 'MSW';
import { mockErrorState } from './mockErrorState';

export const handlers = [
  rest.get('/api/items', (req, res, ctx) => {
    const error = mockErrorState.getError('/api/items');
    if (error === 404) return res(ctx.status(404), ctx.json({ error: 'Not Found' }));
    if (error === 'network') return res.networkError('Network error');
    if (error === 'timeout') return res(ctx.status(408), ctx.json({ error: 'Timeout' }));
    return res(ctx.status(200), ctx.json({ items: [{ id: 1, name: 'Item A' }] }));
  }),
];

3. モック切り替えUIパネル

MockErrorPanel.tsx
import React, { useState } from 'react';
import { mockErrorState } from '../mocks/mockErrorState';

const endpoints = ['/api/items', '/api/users'];

export const MockErrorPanel = () => {
  const [errors, setErrors] = useState(mockErrorState.getAllErrors());
  const setError = (endpoint: string, error: any) => {
    mockErrorState.setError(endpoint, error);
    setErrors(mockErrorState.getAllErrors());
  };
  return (
    <div style={{ position: 'fixed', bottom: 20, right: 20, background: '#fff', border: '1px solid #ccc', padding: 16, zIndex: 1000 }}>
      <h4>Mock Error Panel</h4>
      {endpoints.map((ep) => (
        <div key={ep} style={{ marginBottom: 8 }}>
          <span>{ep}</span>
          <button onClick={() => setError(ep, 404)}>404</button>
          <button onClick={() => setError(ep, 500)}>500</button>
          <button onClick={() => setError(ep, 'network')}>Network</button>
          <button onClick={() => setError(ep, 'timeout')}>Timeout</button>
          <button onClick={() => setError(ep, null)}>Clear</button>
        </div>
      ))}
    </div>
  );
};

4. 拡張ポイント

  • 通常系レスポンスもパネルから切り替え可能にする
  • エンドポイント一覧を動的に取得+フィルタ検索を追加

まとめ

UIからモックレスポンスをリアルタイムに切り替えられると、以下のようなメリットがあります。

  • 開発のスピードがアップする
  • 異常系のテストも自動化しやすくなる
  • エンジニアだけでなく、QAやPdMなども作業がしやすくなる
  • 状態を保存できるので、問題の再現がラクになる

ぜひ本記事を参考に、MSWを活用したUI型モックを導入してみてください。

最後まで読んでいただきありがとうございます。

Discussion