T-CREATOR

Playwright でスマホ・タブレット画面も一発検証

Playwright でスマホ・タブレット画面も一発検証

現代の Web 開発において、レスポンシブデザインはもはや必須要件となっています。しかし、実際にスマートフォンやタブレットでの表示を確認するとなると、開発者の頭を悩ませる問題が山積みです。

「Chrome DevTools で確認したけど、実際のデバイスでは違う見た目になる」 「テスト環境で動作確認したのに、本番でレイアウトが崩れる」 「複数のデバイスサイズを一つずつ確認するのが面倒すぎる」

このような悩みを抱えている開発者の方も多いのではないでしょうか。

Playwright を使えば、これらの問題を一気に解決できます。スマホ・タブレット・デスクトップの画面サイズを自動で切り替えながら、一発で検証できるのです。

この記事では、Playwright のデバイスエミュレーション機能を活用して、効率的なマルチデバイステストを実現する方法をご紹介します。

レスポンシブデザイン検証の重要性

レスポンシブデザインの検証は、現代の Web 開発において最も重要な品質保証の一つです。

なぜマルチデバイステストが必要なのか

ユーザーの約 60%がモバイルデバイスから Web サイトにアクセスしている現在、デスクトップだけでなく、スマートフォンやタブレットでの表示確認は必須です。

従来の手動確認では以下の問題が発生していました:

  • 開発時間の大幅な増加
  • 人的ミスの発生リスク
  • デバイス固有のバグの見落とし
  • 継続的な確認の困難さ

従来の検証方法の限界

javascript// 従来の手動確認の例
// 開発者が一つずつ画面サイズを変更して確認
// 時間がかかり、見落としも多い

// Chrome DevToolsでの手動確認
// 1. F12キーを押す
// 2. デバイスツールバーをクリック
// 3. デバイスを選択
// 4. スクロールして確認
// 5. 次のデバイスで繰り返し...

この方法では、デバイスごとに 10 分程度かかり、5 つのデバイスで確認するだけで 50 分も費やしてしまいます。

Playwright のデバイスエミュレーション機能

Playwright は、豊富なデバイスエミュレーション機能を提供しており、これらを活用することで効率的なテストが可能です。

内蔵デバイスプロファイル

Playwright には、実際のデバイスに基づいたプロファイルが多数用意されています:

javascript// Playwrightの内蔵デバイスプロファイル例
const devices = [
  'iPhone 12',
  'iPhone 12 Pro',
  'iPad Pro',
  'Galaxy S20',
  'Galaxy Tab S7',
  'Pixel 5',
  'Surface Pro 7',
];

デバイスエミュレーションの仕組み

Playwright のデバイスエミュレーションは、実際のデバイスの特性を忠実に再現します:

  • 画面解像度
  • ピクセル密度(DPR)
  • タッチ操作の有無
  • ユーザーエージェント
  • ビューポートサイズ

これにより、実際のデバイスに近い環境でのテストが可能になります。

基本的なデバイス設定方法

Playwright でデバイスエミュレーションを設定する基本的な方法をご紹介します。

プロジェクトの初期設定

まず、Playwright プロジェクトを作成します:

bash# Playwrightプロジェクトの作成
yarn create playwright@latest my-responsive-test
cd my-responsive-test

基本的なデバイス設定

javascript// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  projects: [
    // iPhone 12でのテスト
    {
      name: 'iPhone 12',
      use: { ...devices['iPhone 12'] },
    },
    // iPad Proでのテスト
    {
      name: 'iPad Pro',
      use: { ...devices['iPad Pro'] },
    },
    // Galaxy S20でのテスト
    {
      name: 'Galaxy S20',
      use: { ...devices['Galaxy S20'] },
    },
  ],
});

カスタムデバイス設定

内蔵デバイス以外にも、カスタム設定でデバイスを定義できます:

javascript// カスタムデバイス設定の例
const customDevices = {
  'Custom Tablet': {
    viewport: { width: 1024, height: 768 },
    userAgent:
      'Mozilla/5.0 (iPad; CPU OS 14_7_1 like Mac OS X)',
    deviceScaleFactor: 2,
    hasTouch: true,
    isMobile: true,
  },
  'Custom Mobile': {
    viewport: { width: 375, height: 667 },
    userAgent:
      'Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X)',
    deviceScaleFactor: 3,
    hasTouch: true,
    isMobile: true,
  },
};

複数デバイスでの一括テスト実行

複数のデバイスで同時にテストを実行する方法をご紹介します。

並列実行の設定

javascript// playwright.config.ts
export default defineConfig({
  workers: 3, // 並列実行数
  projects: [
    {
      name: 'iPhone 12',
      use: { ...devices['iPhone 12'] },
    },
    {
      name: 'iPad Pro',
      use: { ...devices['iPad Pro'] },
    },
    {
      name: 'Galaxy S20',
      use: { ...devices['Galaxy S20'] },
    },
  ],
});

基本的なレスポンシブテスト

javascript// tests/responsive.spec.ts
import { test, expect } from '@playwright/test';

test.describe('レスポンシブデザインテスト', () => {
  test('ヘッダーナビゲーションの表示確認', async ({
    page,
  }) => {
    // ページにアクセス
    await page.goto('https://example.com');

    // ヘッダーが表示されていることを確認
    const header = page.locator('header');
    await expect(header).toBeVisible();

    // ナビゲーションメニューの表示確認
    const nav = page.locator('nav');
    await expect(nav).toBeVisible();
  });

  test('メインコンテンツのレイアウト確認', async ({
    page,
  }) => {
    await page.goto('https://example.com');

    // メインコンテンツエリアの確認
    const main = page.locator('main');
    await expect(main).toBeVisible();

    // コンテンツが適切に配置されているか確認
    const { width } = await page.viewportSize();
    if (width < 768) {
      // モバイル表示の場合
      await expect(
        page.locator('.mobile-menu')
      ).toBeVisible();
    } else {
      // デスクトップ表示の場合
      await expect(
        page.locator('.desktop-menu')
      ).toBeVisible();
    }
  });
});

テスト実行コマンド

bash# 全デバイスでテスト実行
yarn playwright test

# 特定のデバイスでのみ実行
yarn playwright test --project="iPhone 12"

# ヘッドレスモードで実行(CI/CD用)
yarn playwright test --headed=false

実際のプロジェクトでの活用例

実際のプロジェクトで Playwright を活用した事例をご紹介します。

E コマースサイトでの活用例

javascript// tests/ecommerce.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Eコマースサイト レスポンシブテスト', () => {
  test('商品一覧ページの表示確認', async ({ page }) => {
    await page.goto('/products');

    // 商品カードの表示確認
    const productCards = page.locator('.product-card');
    await expect(productCards.first()).toBeVisible();

    // グリッドレイアウトの確認
    const { width } = await page.viewportSize();
    if (width < 768) {
      // モバイル:1列表示
      await expect(productCards).toHaveCount(1);
    } else if (width < 1024) {
      // タブレット:2列表示
      await expect(productCards).toHaveCount(2);
    } else {
      // デスクトップ:3列表示
      await expect(productCards).toHaveCount(3);
    }
  });

  test('カート機能の動作確認', async ({ page }) => {
    await page.goto('/products/1');

    // 商品詳細の表示確認
    await expect(
      page.locator('.product-title')
    ).toBeVisible();
    await expect(
      page.locator('.product-price')
    ).toBeVisible();

    // カートに追加ボタンの確認
    const addToCartButton = page.locator('.add-to-cart');
    await expect(addToCartButton).toBeVisible();

    // カートに追加
    await addToCartButton.click();

    // カートアイコンの更新確認
    await expect(page.locator('.cart-count')).toHaveText(
      '1'
    );
  });
});

よくあるエラーと対処法

実際の開発で遭遇する可能性のあるエラーとその解決方法をご紹介します。

エラー 1: 要素が見つからない

javascript// エラー例
// Error: locator.click: Target closed
// 原因:ページが読み込まれる前に要素をクリックしようとした

// 解決方法
await page.waitForLoadState('networkidle');
await page.locator('.button').click();

エラー 2: タイムアウトエラー

javascript// エラー例
// Error: Timeout 30000ms exceeded.
// 原因:要素の読み込みに時間がかかりすぎている

// 解決方法
await page
  .locator('.slow-loading-element')
  .waitFor({ timeout: 60000 });

エラー 3: ビューポートサイズの問題

javascript// エラー例
// Error: Viewport size is not supported
// 原因:設定したビューポートサイズが無効

// 解決方法
await page.setViewportSize({ width: 375, height: 667 });

デバイス固有のテストケース作成

各デバイスに特有のテストケースを作成する方法をご紹介します。

タッチ操作のテスト

javascript// tests/touch-interactions.spec.ts
import { test, expect } from '@playwright/test';

test.describe('タッチ操作テスト', () => {
  test('スワイプ操作の確認', async ({ page }) => {
    await page.goto('/mobile-gallery');

    // 画像ギャラリーの表示確認
    const gallery = page.locator('.image-gallery');
    await expect(gallery).toBeVisible();

    // スワイプ操作のシミュレーション
    await page.touchscreen.swipe(300, 400, 100, 400);

    // 次の画像が表示されることを確認
    await expect(page.locator('.image-2')).toBeVisible();
  });

  test('ピンチズームの確認', async ({ page }) => {
    await page.goto('/map');

    // マップの表示確認
    const map = page.locator('.map-container');
    await expect(map).toBeVisible();

    // ピンチズームのシミュレーション
    await page.touchscreen.pinch(400, 300, 1.5);

    // ズームレベルが変更されることを確認
    await expect(page.locator('.zoom-level')).toContainText(
      '150%'
    );
  });
});

モバイル特有の機能テスト

javascript// tests/mobile-specific.spec.ts
test.describe('モバイル特有機能テスト', () => {
  test('ハンバーガーメニューの動作確認', async ({
    page,
  }) => {
    await page.goto('/');

    // ハンバーガーメニューボタンの確認
    const menuButton = page.locator('.hamburger-menu');
    await expect(menuButton).toBeVisible();

    // メニューを開く
    await menuButton.click();

    // メニューが表示されることを確認
    const mobileMenu = page.locator('.mobile-menu');
    await expect(mobileMenu).toBeVisible();

    // メニュー項目の確認
    await expect(page.locator('.menu-item')).toHaveCount(5);
  });

  test('プルリフレッシュの確認', async ({ page }) => {
    await page.goto('/feed');

    // フィードの表示確認
    const feed = page.locator('.feed-container');
    await expect(feed).toBeVisible();

    // プルリフレッシュのシミュレーション
    await page.touchscreen.swipe(200, 100, 200, 300);

    // リフレッシュインジケーターの確認
    await expect(
      page.locator('.refresh-indicator')
    ).toBeVisible();
  });
});

パフォーマンスと実行時間の最適化

テストの実行時間を短縮し、効率を向上させる方法をご紹介します。

並列実行の最適化

javascript// playwright.config.ts
export default defineConfig({
  workers: process.env.CI ? 4 : 2, // CI環境では並列数を増やす
  timeout: 30000,
  expect: {
    timeout: 5000,
  },
  projects: [
    {
      name: 'iPhone 12',
      use: { ...devices['iPhone 12'] },
    },
    {
      name: 'iPad Pro',
      use: { ...devices['iPad Pro'] },
    },
    {
      name: 'Galaxy S20',
      use: { ...devices['Galaxy S20'] },
    },
  ],
});

テストデータの共有

javascript// tests/shared-setup.spec.ts
import { test as base } from '@playwright/test';

// 共通のセットアップを定義
const test = base.extend({
  authenticatedPage: async ({ page }, use) => {
    // ログイン処理
    await page.goto('/login');
    await page.fill('#email', 'test@example.com');
    await page.fill('#password', 'password123');
    await page.click('#login-button');
    await page.waitForURL('/dashboard');

    await use(page);
  },
});

export { test, expect } from '@playwright/test';

キャッシュの活用

javascript// tests/performance.spec.ts
import { test, expect } from '@playwright/test';

test.describe('パフォーマンス最適化テスト', () => {
  test.beforeEach(async ({ page }) => {
    // キャッシュを有効にする
    await page.route('**/*', (route) => {
      if (route.request().resourceType() === 'image') {
        route.continue();
      } else {
        route.continue();
      }
    });
  });

  test('画像の遅延読み込み確認', async ({ page }) => {
    await page.goto('/gallery');

    // 最初に表示される画像のみ読み込み確認
    const visibleImages = page.locator(
      'img[loading="lazy"]'
    );
    await expect(visibleImages.first()).toBeVisible();

    // スクロールして追加画像を読み込み
    await page.evaluate(() => window.scrollTo(0, 1000));
    await page.waitForTimeout(1000);

    // 追加画像の読み込み確認
    await expect(visibleImages.nth(5)).toBeVisible();
  });
});

CI/CD 環境での最適化

yaml# .github/workflows/playwright.yml
name: Playwright Tests
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 18
      - name: Install dependencies
        run: yarn install
      - name: Install Playwright Browsers
        run: yarn playwright install
      - name: Run Playwright tests
        run: yarn playwright test --reporter=html
      - name: Upload test results
        uses: actions/upload-artifact@v3
        if: always()
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30

まとめ

Playwright のデバイスエミュレーション機能を活用することで、スマートフォンやタブレットでの表示確認を効率的に行うことができます。

この記事でご紹介した方法により、以下のメリットを享受できるようになります:

  • 時間の大幅な短縮: 手動確認の 50 分が自動化により 5 分に短縮
  • 品質の向上: 人的ミスの排除と網羅的なテスト
  • 継続的な検証: CI/CD パイプラインでの自動実行
  • 開発効率の向上: 早期のバグ発見と修正

実際にプロジェクトに導入する際は、まずは基本的なデバイス設定から始めて、段階的にテストケースを拡充していくことをお勧めします。

Playwright の強力な機能を活用して、レスポンシブデザインの品質保証を自動化し、ユーザーに最高の体験を提供できる Web サイトを作成しましょう。

関連リンク