Testing Architecture

Asagiri Design System - テスト設計アーキテクチャ完全ガイド

← ドキュメントに戻る API Reference

テスト戦略の概要

Asagiriは、高品質なCSSフレームワークを保証するため、複数の層でテストを実施します。

テストピラミッド

Critical

ビジュアルリグレッションテスト

UIの意図しない変更を検出

  • Chromatic
  • Percy
  • BackstopJS
Critical

アクセシビリティテスト

WCAG 2.1 AA準拠を保証

  • axe-core
  • WAVE
  • pa11y
High

クロスブラウザテスト

主要ブラウザでの互換性

  • BrowserStack
  • Sauce Labs
  • Playwright
High

パフォーマンステスト

ロード時間とレンダリング最適化

  • Lighthouse
  • WebPageTest
  • Bundle Size
Medium

デザイントークンテスト

CSS変数の整合性チェック

  • Token Validator
  • Contrast Checker
Medium

SCSSユニットテスト

関数とミックスインの検証

  • True (Sass testing)
  • Jest + Sass
テスト自動化の目標:
  • コミット前: ローカルリントとフォーマットチェック
  • PR作成時: ビジュアルリグレッションとアクセシビリティテスト
  • マージ前: 全テストスイート実行
  • リリース時: パフォーマンス監視とバンドルサイズチェック

ビジュアルリグレッションテスト

UIの予期しない変更を自動的に検出するための重要なテスト層です。

Chromatic(推奨)

# インストール
npm install --save-dev chromatic

# package.json にスクリプト追加
{
  "scripts": {
    "chromatic": "chromatic --project-token=<your-token>"
  }
}

# 実行
npm run chromatic

Percy(GitHub Actions統合)

# .github/workflows/visual-tests.yml
name: Visual Tests
on: [pull_request]

jobs:
  percy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npm run build
      - name: Percy Test
        run: npx percy snapshot ./showcase.html
        env:
          PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}

BackstopJS(セルフホスト)

// backstop.config.js
module.exports = {
  id: "asagiri",
  viewports: [
    {
      label: "phone",
      width: 375,
      height: 667
    },
    {
      label: "tablet",
      width: 768,
      height: 1024
    },
    {
      label: "desktop",
      width: 1920,
      height: 1080
    }
  ],
  scenarios: [
    {
      label: "Components Showcase",
      url: "http://localhost:8080/showcase.html",
      selectors: [".btn", ".card", ".alert"],
      delay: 500
    },
    {
      label: "Dark Mode",
      url: "http://localhost:8080/showcase.html",
      selectors: ["body"],
      onBeforeScript: "dark-mode.js"
    }
  ],
  paths: {
    bitmaps_reference: "backstop_data/bitmaps_reference",
    bitmaps_test: "backstop_data/bitmaps_test",
    html_report: "backstop_data/html_report"
  }
};
ベストプラクティス:
  • コンポーネント単位でスナップショットを取得
  • ライトモードとダークモードの両方をテスト
  • 複数の画面サイズでテスト(モバイル、タブレット、デスクトップ)
  • アニメーション完了後にスナップショット取得

アクセシビリティテスト

WCAG 2.1 AA準拠を保証し、すべてのユーザーが利用可能なUIを提供します。

axe-core(自動テスト)

// accessibility.test.js
const { AxePuppeteer } = require('@axe-core/puppeteer');
const puppeteer = require('puppeteer');

describe('Accessibility Tests', () => {
  let browser;
  let page;

  beforeAll(async () => {
    browser = await puppeteer.launch();
    page = await browser.newPage();
  });

  afterAll(async () => {
    await browser.close();
  });

  test('Components showcase should be accessible', async () => {
    await page.goto('http://localhost:8080/showcase.html');
    const results = await new AxePuppeteer(page).analyze();

    expect(results.violations).toHaveLength(0);
  });

  test('Dark mode should be accessible', async () => {
    await page.goto('http://localhost:8080/showcase.html');
    await page.evaluate(() => {
      document.documentElement.setAttribute('data-theme', 'dark');
    });

    const results = await new AxePuppeteer(page).analyze();
    expect(results.violations).toHaveLength(0);
  });
});

pa11y(CI統合)

// pa11y.config.js
module.exports = {
  standard: 'WCAG2AA',
  urls: [
    'http://localhost:8080/showcase.html',
    'http://localhost:8080/docs/getting-started.html'
  ],
  chromeLaunchConfig: {
    args: ['--no-sandbox']
  },
  runners: ['axe', 'htmlcs'],
  threshold: 0 // 0 violations allowed
};

// package.json
{
  "scripts": {
    "test:a11y": "pa11y-ci --config pa11y.config.js"
  }
}

コントラスト比チェック

// contrast-check.js
const Color = require('colorjs.io');

const colorPairs = [
  { bg: '#ffffff', fg: '#4c5059', name: 'Text on Light' },
  { bg: '#0f0f0f', fg: '#e4e4e7', name: 'Text on Dark' },
  { bg: '#31a9c7', fg: '#ffffff', name: 'Primary Button' }
];

colorPairs.forEach(pair => {
  const ratio = Color.contrastRatio(pair.bg, pair.fg);
  const wcagAA = ratio >= 4.5;
  const wcagAAA = ratio >= 7;

  console.log(`${pair.name}: ${ratio.toFixed(2)}:1`);
  console.log(`  WCAG AA: ${wcagAA ? 'PASS' : 'FAIL'}`);
  console.log(`  WCAG AAA: ${wcagAAA ? 'PASS' : 'FAIL'}`);
});
チェック項目:
  • テキストコントラスト比: 4.5:1 以上(WCAG AA)
  • 大きいテキスト: 3:1 以上
  • UIコンポーネント: 3:1 以上
  • キーボードナビゲーション対応
  • スクリーンリーダー互換性
  • focus indicatorの視認性

クロスブラウザテスト

主要ブラウザ(Chrome、Firefox、Safari、Edge)での互換性を保証します。

Playwright(推奨)

// playwright.config.js
module.exports = {
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] }
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] }
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] }
    },
    {
      name: 'Mobile Chrome',
      use: { ...devices['Pixel 5'] }
    },
    {
      name: 'Mobile Safari',
      use: { ...devices['iPhone 13'] }
    }
  ]
};

// tests/cross-browser.spec.js
test.describe('Cross Browser Tests', () => {
  test('buttons render correctly', async ({ page }) => {
    await page.goto('http://localhost:8080/showcase.html');

    const button = page.locator('.btn-primary').first();
    await expect(button).toBeVisible();
    await expect(button).toHaveCSS('background-color', 'rgb(49, 169, 199)');
  });
});

BrowserStack(実機テスト)

// browserstack.config.js
exports.config = {
  user: process.env.BROWSERSTACK_USERNAME,
  key: process.env.BROWSERSTACK_ACCESS_KEY,

  capabilities: [
    {
      browserName: 'Chrome',
      browser_version: 'latest',
      os: 'Windows',
      os_version: '11'
    },
    {
      browserName: 'Safari',
      browser_version: 'latest',
      os: 'OS X',
      os_version: 'Ventura'
    },
    {
      browserName: 'Edge',
      browser_version: 'latest',
      os: 'Windows',
      os_version: '11'
    }
  ]
};
テスト対象ブラウザ:
  • Chrome (最新版 + 2つ前のバージョン)
  • Firefox (最新版 + ESR)
  • Safari (最新版 + 1つ前のバージョン)
  • Edge (最新版)
  • モバイル: iOS Safari, Chrome for Android

パフォーマンステスト

高速なロード時間とスムーズなレンダリングを保証します。

Lighthouse CI

// lighthouserc.js
module.exports = {
  ci: {
    collect: {
      url: [
        'http://localhost:8080/showcase.html',
        'http://localhost:8080/docs/getting-started.html'
      ],
      numberOfRuns: 3
    },
    assert: {
      assertions: {
        'categories:performance': ['error', { minScore: 0.9 }],
        'categories:accessibility': ['error', { minScore: 0.95 }],
        'categories:best-practices': ['error', { minScore: 0.9 }],
        'first-contentful-paint': ['error', { maxNumericValue: 2000 }],
        'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
        'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }]
      }
    },
    upload: {
      target: 'temporary-public-storage'
    }
  }
};

Bundle Size Check

// package.json
{
  "scripts": {
    "size": "size-limit",
    "size:why": "size-limit --why"
  }
}

// .size-limit.js
module.exports = [
  {
    name: "Asagiri Full",
    path: "dist/asagiri.min.css",
    limit: "50 KB",
    gzip: true
  },
  {
    name: "Asagiri Core",
    path: "dist/asagiri.core.min.css",
    limit: "20 KB",
    gzip: true
  }
];

パフォーマンス目標

指標 目標値 説明
FCP < 2.0s First Contentful Paint
LCP < 2.5s Largest Contentful Paint
CLS < 0.1 Cumulative Layout Shift
TTI < 3.8s Time to Interactive
Bundle Size < 50 KB (gzip) Full framework size

SCSSユニットテスト

Sassの関数、ミックスイン、ロジックをテストします。

True(Sassテストフレームワーク)

// tests/scss/_colors.scss
@use 'true' as *;
@use '../../scss/Tokens/Color' as *;

@include test-module('Color Functions') {
  @include test('color-mix function') {
    @include assert-equal(
      color-mix(in srgb, red 50%, blue),
      #8000ff,
      'Should mix colors correctly'
    );
  }

  @include test('CSS variable generation') {
    @include assert-true(
      variable-exists(color-primary),
      'Primary color variable should exist'
    );
  }
}

Jest + Sass

// tests/design-tokens.test.js
const sass = require('sass');
const path = require('path');

describe('Design Tokens', () => {
  let compiledCSS;

  beforeAll(() => {
    const result = sass.compile(
      path.join(__dirname, '../scss/main.scss')
    );
    compiledCSS = result.css.toString();
  });

  test('Primary color is defined', () => {
    expect(compiledCSS).toContain('--color-primary');
  });

  test('Spacing scale is consistent', () => {
    const spacingVars = [
      '--spacing-1', '--spacing-2', '--spacing-3',
      '--spacing-4', '--spacing-5', '--spacing-6'
    ];

    spacingVars.forEach(variable => {
      expect(compiledCSS).toContain(variable);
    });
  });

  test('Dark theme overrides are present', () => {
    expect(compiledCSS).toContain('[data-theme="dark"]');
  });
});

CI/CD パイプライン

GitHub Actionsを使用した自動テストとデプロイメント。

完全なテストパイプライン

# .github/workflows/test.yml
name: Test Suite

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npm run lint:scss
      - run: npm run lint:css

  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npm run build
      - run: npm run size

  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npm test

  visual-regression:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npm run build
      - run: npm run chromatic
        env:
          CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_TOKEN }}

  accessibility:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npm run build
      - run: npm start &
      - run: npm run test:a11y

  cross-browser:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npm run test:cross-browser

  performance:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npm run build
      - run: npm start &
      - run: npm run lighthouse
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_TOKEN }}

ベストプラクティス

テストを成功させるための推奨事項

  • 小さく始める: 最も重要なコンポーネントから優先的にテスト
  • 自動化する: CI/CDパイプラインに統合して手動テストを削減
  • 定期的に実行: PR作成時、マージ前、リリース前に必ず実行
  • メトリクスを追跡: テストカバレッジとパフォーマンス指標をモニタリング
  • 失敗を学習に変える: テスト失敗時は原因を分析してドキュメント化

よくある落とし穴

  • 過剰なテスト: すべてをテストしようとしてメンテナンスコストが増大
  • フレイキーテスト: 不安定なテストは信頼性を損なう
  • 遅いテスト: 実行時間が長いとCI/CDがボトルネックに
  • outdatedスナップショット: 定期的な更新を忘れない

推奨ツールスタック

カテゴリ ツール 理由
ビジュアル Chromatic Storybook統合、PR自動コメント
アクセシビリティ axe-core + pa11y 業界標準、CI/CD対応
クロスブラウザ Playwright 高速、信頼性高い、実機不要
パフォーマンス Lighthouse CI 無料、詳細なレポート
ユニットテスト Jest + True JavaScriptとSassの両対応

次のステップ