🏆

PlaywrightでAPIをモックする 〜PokéAPIでヌオーさんを優勝させる〜

2023/07/31に公開

はじめに

バックエンドのAPIからデータを取得し、フロントエンドでVueやReactを使って表示する、というケースはよくあると思います。私の職場にはよくあります。

こういう場合のテストで、APIの値を好きにいじることができるとテストが捗ります。
そんな時のために、E2EテストのフレームワークであるPlaywrightはAPIモックを提供しています。

https://playwright.dev/

この記事は、PlaywrightでのAPIモックの方法を、ポケモンの情報が取れるPokéAPIをモックし、すべてをヌオーさんにしながら学んでいく、そんな記事です。

PokéAPI
https://pokeapi.co/

ヌオーさん
https://zukan.pokemon.co.jp/detail/0195

サンプル用Reactアプリ

ほとんどテンプレートのままですが、PokéAPIを使ってポケモン情報を取得・表示するReactアプリを作りました。

npm init vite playwright_api_mock

Playwrightもセットアップします。

npm init playwright@latest

playwright.config.tsuse.screenshotをtrueにしておきます。

App.tsxは次のようにしました。

App.tsx
import { useEffect, useState } from 'react';
import axios from 'axios';
import { Pokemon } from './types';

function App() {
  const [pokemon, setPokemon] = useState<Pokemon>();
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    axios.get('https://pokeapi.co/api/v2/pokemon/greninja')
      .then(response => {
        console.log(JSON.stringify(response.data));
        setPokemon(response.data);
        setLoading(false);
      })
      .catch(error => {
        setError(error.message);
        setLoading(false);
      });
  }, []);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <div className="App">
      <h1>ポケモン人気ランキング</h1>
      <ul>
          <li key={pokemon!.id}>
            <h2>ポケモン人気ランキング第1位は{pokemon!.name}!</h2>
            <img src={pokemon!.sprites.front_default} alt={pokemon!.name} />
          </li>
      </ul>
    </div>
  );
}

export default App;

Pokemon型は下記。

types.ts
export interface Pokemon {
  id: number;
  name: string;
  sprites: {
    front_default: string;
  }
}

ローカルでviteを立ち上げます。

npx vite --port=4000

http://localhost:4000/にアクセスすると、https://pokeapi.co/api/v2/pokemon/greninjaから情報を取得、下記のように表示されます。greninjaはゲッコウガさんです。

このAPIをモックして、人気ポケモンをヌオーさんにしていきたいと思います。

PlaywrightのAPIモック

PlaywrightでのAPIモックの方法はドキュメントの下記で説明されています。

https://playwright.dev/docs/mock

apiMockSample.ts
test("mocks a fruit and doesn't call api", async ({ page }) => {
  // Mock the api call before navigating
  await page.route('*/**/api/v1/fruits', async route => {
    const json = [{ name: 'Strawberry', id: 21 }];
    await route.fulfill({ json });
  });
  // Go to the page
  await page.goto('https://demo.playwright.dev/api-mocking');

  // Assert that the Strawberry fruit is visible
  await expect(page.getByText('Strawberry')).toBeVisible();
});

このサンプルコードの要はここ。

  await page.route('*/**/api/v1/fruits', async route => {
    const json = [{ name: 'Strawberry', id: 21 }];
    await route.fulfill({ json });
  });

fulfillによって、レスポンスを操作することができます。JSONデータを与えたいときは、引数にjsonオブジェクトを与えます。

https://playwright.dev/docs/api/class-route#route-fulfill

サンプルアプリでモックしてみる

さっそくヌオーさんを人気ポケモンにしましょう。
tests/mockemon.spec.tsを作成します。

ヌオーさんは英語でQuagsireといいます。沼(quagmire)の王さま(sire)です。

mockemon.spec.ts
import { test, expect } from '@playwright/test';
import { Pokemon } from '../src/types';

test("ヌオーさんしか勝たん", async ({ page }) => {
  const mockemon: Pokemon = {
    id: 195,
    name: 'quagsire',
    sprites: {
      front_default: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/195.png'
    }
  }

  await page.route('https://pokeapi.co/api/v2/pokemon/greninja', async route => {
    await route.fulfill({ json: mockemon });
  });

  await page.goto('http://localhost:4000/');

  await expect(page.getByText('quagsire')).toBeVisible();
});

ヌオーさんのモックデータ(mockemon)を用意し、routeにfulfillでセットします。

これでテストを実行してみます。

npx playwright test mockemon.spec.ts 


結果。ヌオーさんしか勝たん。

念のため、reportを開いてスクリーンショットも見てみます。

npx playwright show-report


ハロー、ヌオーさん!


App.tsxのリクエストURLをgreninjaから、pikachuに変えてみましょう。

App.tsx
+    axios.get('https://pokeapi.co/api/v2/pokemon/pikachu')
-    axios.get('https://pokeapi.co/api/v2/pokemon/greninja')

localhost:4000はピカチュウのものとなりました。

テストを実行すると、failします。許せません。

ヌオーさんが電気ネズミ風情に負けないように、テストコードを修正します。
routeの引数には正規表現やglobパターンも使えますので、globで書き換えます。

mockemon.spec.ts
+    await page.route('https://pokeapi.co/api/v2/pokemon/**', async route => {
-    await page.route('https://pokeapi.co/api/v2/pokemon/grenja', async route => {

もう一度テストを実行します。

おわりに

というわけで、PokéAPIを例にplaywrightでのAPIモックを行いました。
PlaywrightでのAPIモックは簡単で、しかもVueやReactでのバックエンド通信をテストする際など、実務でもかなり便利です。

最後に本記事のまとめです。これだけ覚えればAPIモックができます。ぜひ覚えて帰ってください。

Discussion