T-CREATOR

Emotion と Jest/Testing Library で UI テストを快適に

Emotion と Jest/Testing Library で UI テストを快適に

モダンな React 開発において、CSS-in-JS ライブラリの Emotion と Jest・Testing Library を組み合わせた UI テストは、もはや必須のスキルとなっています。しかし、従来の CSS ファイルベースのテストとは異なる課題や複雑さに直面することも多いでしょう。

本記事では、Emotion を使ったコンポーネントのテストを効率的に行うための手法を、基礎から応用まで段階的に解説いたします。初心者の方でも理解しやすいよう、具体的なコード例とともに実践的なテスト手法をご紹介します。

背景

CSS-in-JS ライブラリ Emotion とは

Emotion は、JavaScript を使ってスタイルを記述できる人気の CSS-in-JS ライブラリです。React コンポーネントと密結合したスタイリングが可能で、動的なスタイル変更やテーマシステムの実装が容易になります。

Emotion の基本的な使用例をご覧ください。

javascriptimport styled from '@emotion/styled';

const Button = styled.button`
  background-color: ${props => props.primary ? '#007bff' : '#6c757d'};
  color: white;
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
`;

このように、props に応じて動的にスタイルを変更できる柔軟性が Emotion の大きな魅力です。

UI テストにおける従来の課題

従来の CSS ファイルベースのスタイリングでは、テスト時にスタイルの適用状況を確認する際、以下のような課題がありました。

  • CSS クラス名が固定的で、動的なスタイル変更のテストが困難
  • テーマやブランド変更時のスタイル影響範囲の把握が難しい
  • レスポンシブデザインの各ブレークポイントでのテスト実装の複雑さ

これらの課題により、UI の品質担保が不十分になりがちでした。

Jest と Testing Library の役割

Jest は JavaScript のテストランナーとして、Testing Library は React コンポーネントのテストユーティリティとして、それぞれ重要な役割を果たします。

Jest の主な機能には以下があります。

javascript// Jest の基本的なテスト構造
describe('Button コンポーネント', () => {
  test('primary プロパティが true の時、青色の背景になる', () => {
    // テストの実装
  });
});

Testing Library は、ユーザーの行動に近い形でのテストを可能にします。

javascriptimport { render, screen } from '@testing-library/react';

// ユーザー視点でのコンポーネントテスト
const button = screen.getByRole('button', { name: '送信' });

この組み合わせにより、Emotion で作られたコンポーネントも効果的にテストできるのです。

課題

Emotion のスタイルが適用されたコンポーネントのテスト難易度

Emotion を使用したコンポーネントのテストでは、従来の CSS クラスベースのテストとは異なる複雑さが生まれます。

以下の図は、Emotion でのスタイル適用とテスト実行の流れを示しています。

mermaidflowchart TD
  A[コンポーネント定義] --> B[Emotion スタイル注入]
  B --> C[DOM 生成]
  C --> D[テスト実行]
  D --> E{スタイル検証}
  E -->|成功| F[テスト通過]
  E -->|失敗| G[デバッグ必要]
  G --> H[クラス名・属性確認]
  H --> D

図で理解できる要点:

  • Emotion はランタイムでスタイルを注入するため、テスト時のタイミングが重要
  • 従来の固定クラス名と異なり、動的に生成されるクラス名への対応が必要
  • デバッグ時は生成されたクラス名や属性を確認する工程が追加される

主な課題として、以下の点が挙げられます。

動的クラス名の問題
Emotion は実行時に一意のクラス名を生成するため、テストでのスタイル検証が困難です。例えば、css-1234abcd のような予測不可能なクラス名が生成されます。

スタイル注入のタイミング
Emotion のスタイル注入は JavaScript の実行時に行われるため、Jest のテスト環境でのスタイル適用タイミングを正確に制御する必要があります。

スタイル変更時のテスト保守性

コンポーネントのスタイルを変更した際、関連するテストも同時に更新する必要があります。しかし、Emotion の動的な性質により、テストの保守が複雑になる場合があります。

変更内容影響範囲必要な対応
色の変更該当コンポーネントカラーテスト更新
サイズ調整レイアウト関連サイズ・余白テスト更新
アニメーション追加動的動作新規テストケース追加

動的スタイルのテスト検証方法

props や state に応じて動的に変化するスタイルのテストは、特に複雑です。以下のようなケースでの検証方法を確立する必要があります。

javascript// 検証が困難な動的スタイルの例
const DynamicButton = styled.button`
  background-color: ${props => {
    if (props.loading) return '#ccc';
    if (props.error) return '#dc3545';
    if (props.success) return '#28a745';
    return '#007bff';
  }};
  opacity: ${props => props.disabled ? 0.6 : 1};
  transform: ${props => props.pressed ? 'scale(0.95)' : 'scale(1)'};
`;

このような複雑な条件分岐を含むスタイルの全パターンをテストするには、体系的なアプローチが求められます。

解決策

Emotion と Jest の連携設定

Emotion を Jest 環境で適切に動作させるための設定から始めましょう。まず、必要なパッケージをインストールします。

bashyarn add --dev @emotion/jest jest-environment-jsdom

次に、Jest の設定ファイルを作成します。

javascript// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'],
  moduleNameMapping: {
    '^@/(.*)$': '<rootDir>/src/$1'
  },
  transform: {
    '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', {
      presets: [
        '@babel/preset-env',
        ['@babel/preset-react', { runtime: 'automatic' }],
        '@babel/preset-typescript'
      ]
    }]
  }
};

setupTests.js ファイルで Emotion のテスト用設定を行います。

javascript// src/setupTests.js
import '@testing-library/jest-dom';
import { matchers } from '@emotion/jest';

// Emotion の custom matchers を追加
expect.extend(matchers);

// CSS-in-JS のスナップショット用設定
import { createSerializer } from '@emotion/jest';
expect.addSnapshotSerializer(createSerializer());

この設定により、Emotion のスタイルを Jest で適切にテストできる環境が整います。

Testing Library でのスタイルテスト手法

Testing Library を使用して、Emotion のスタイルを効果的にテストする方法をご紹介します。基本的なアプローチは以下の通りです。

javascriptimport { render, screen } from '@testing-library/react';
import { ThemeProvider } from '@emotion/react';

const theme = {
  colors: {
    primary: '#007bff',
    secondary: '#6c757d'
  }
};

const renderWithTheme = (ui) => {
  return render(
    <ThemeProvider theme={theme}>
      {ui}
    </ThemeProvider>
  );
};

スタイルの検証には、複数のアプローチが利用できます。

computed style による検証

javascripttest('primary ボタンが正しい背景色を持つ', () => {
  renderWithTheme(<Button primary>テスト</Button>);
  
  const button = screen.getByRole('button');
  const computedStyle = window.getComputedStyle(button);
  
  expect(computedStyle.backgroundColor).toBe('rgb(0, 123, 255)');
});

data 属性を使用した検証

javascriptconst Button = styled.button`
  background-color: ${props => props.primary ? '#007bff' : '#6c757d'};
`;

// テスト用の属性を追加
const TestableButton = styled(Button).attrs(props => ({
  'data-variant': props.primary ? 'primary' : 'secondary'
}))``;

test('ボタンのバリアントが正しく設定される', () => {
  renderWithTheme(<TestableButton primary>テスト</TestableButton>);
  
  const button = screen.getByRole('button');
  expect(button).toHaveAttribute('data-variant', 'primary');
});

カスタムマッチャーの活用

@emotion/jest が提供するカスタムマッチャーを活用することで、より直感的なテストが書けます。

javascripttest('Emotion スタイルが正しく適用される', () => {
  const Button = styled.button`
    color: red;
    background-color: blue;
  `;
  
  render(<Button>テスト</Button>);
  const button = screen.getByRole('button');
  
  // Emotion 専用のマッチャーを使用
  expect(button).toHaveStyleRule('color', 'red');
  expect(button).toHaveStyleRule('background-color', 'blue');
});

props に基づく条件付きスタイルのテストも可能です。

javascripttest('primary プロパティに応じてスタイルが変化する', () => {
  const ConditionalButton = styled.button`
    color: ${props => props.primary ? 'white' : 'black'};
    background-color: ${props => props.primary ? 'blue' : 'gray'};
  `;
  
  // primary=true の場合
  const { rerender } = render(<ConditionalButton primary>テスト</ConditionalButton>);
  let button = screen.getByRole('button');
  expect(button).toHaveStyleRule('color', 'white');
  expect(button).toHaveStyleRule('background-color', 'blue');
  
  // primary=false の場合
  rerender(<ConditionalButton>テスト</ConditionalButton>);
  button = screen.getByRole('button');
  expect(button).toHaveStyleRule('color', 'black');
  expect(button).toHaveStyleRule('background-color', 'gray');
});

以下の図は、カスタムマッチャーを活用したテストフローを示しています。

mermaidsequenceDiagram
  participant Test as テストケース
  participant Render as レンダリング
  participant Emotion as Emotion エンジン
  participant DOM as DOM
  participant Matcher as カスタムマッチャー
  
  Test->>Render: コンポーネントレンダリング
  Render->>Emotion: スタイル処理要求
  Emotion->>DOM: CSS ルール注入
  Test->>Matcher: toHaveStyleRule 実行
  Matcher->>DOM: スタイルルール検証
  DOM-->>Matcher: 適用スタイル情報
  Matcher-->>Test: 検証結果

図で理解できる要点:

  • カスタムマッチャーは DOM に注入されたスタイルルールを直接検証
  • Emotion エンジンが処理したスタイルを正確に取得可能
  • テストコードの可読性と保守性が向上

これらのアプローチを組み合わせることで、Emotion を使ったコンポーネントのスタイルを確実にテストできるようになります。

具体例

基本的なスタイルコンポーネントのテスト

まずは、シンプルなボタンコンポーネントのテストから始めましょう。基本的なスタイルプロパティの検証方法をご覧ください。

typescript// Button.tsx
import styled from '@emotion/styled';

interface ButtonProps {
  variant?: 'primary' | 'secondary' | 'danger';
  size?: 'small' | 'medium' | 'large';
  disabled?: boolean;
}

const Button = styled.button<ButtonProps>`
  padding: ${props => {
    switch (props.size) {
      case 'small': return '4px 8px';
      case 'large': return '12px 24px';
      default: return '8px 16px';
    }
  }};
  
  background-color: ${props => {
    if (props.disabled) return '#e9ecef';
    switch (props.variant) {
      case 'primary': return '#007bff';
      case 'danger': return '#dc3545';
      default: return '#6c757d';
    }
  }};
  
  color: ${props => props.disabled ? '#6c757d' : 'white'};
  border: none;
  border-radius: 4px;
  cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'};
  
  &:hover:not(:disabled) {
    opacity: 0.8;
  }
`;

export default Button;

このコンポーネントに対するテストコードは以下のようになります。

typescript// Button.test.tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Button from './Button';

describe('Button コンポーネント', () => {
  test('デフォルトスタイルが正しく適用される', () => {
    render(<Button>デフォルトボタン</Button>);
    
    const button = screen.getByRole('button');
    
    expect(button).toHaveStyleRule('padding', '8px 16px');
    expect(button).toHaveStyleRule('background-color', '#6c757d');
    expect(button).toHaveStyleRule('color', 'white');
    expect(button).toHaveStyleRule('cursor', 'pointer');
  });

  test('variant プロパティに応じて背景色が変化する', () => {
    const { rerender } = render(<Button variant="primary">プライマリ</Button>);
    let button = screen.getByRole('button');
    expect(button).toHaveStyleRule('background-color', '#007bff');

    rerender(<Button variant="danger">デンジャー</Button>);
    button = screen.getByRole('button');
    expect(button).toHaveStyleRule('background-color', '#dc3545');
  });

  test('size プロパティに応じてパディングが変化する', () => {
    const { rerender } = render(<Button size="small">小さいボタン</Button>);
    let button = screen.getByRole('button');
    expect(button).toHaveStyleRule('padding', '4px 8px');

    rerender(<Button size="large">大きいボタン</Button>);
    button = screen.getByRole('button');
    expect(button).toHaveStyleRule('padding', '12px 24px');
  });
});

プロパティベースの動的スタイルテスト

より複雑な動的スタイルの例として、ローディング状態やエラー状態を持つコンポーネントをテストしてみましょう。

typescript// StatusButton.tsx
import styled from '@emotion/styled';
import { keyframes } from '@emotion/react';

const spin = keyframes`
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
`;

interface StatusButtonProps {
  loading?: boolean;
  success?: boolean;
  error?: boolean;
  children: React.ReactNode;
}

const StatusButton = styled.button<StatusButtonProps>`
  position: relative;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  background-color: ${props => {
    if (props.loading) return '#ffc107';
    if (props.success) return '#28a745';
    if (props.error) return '#dc3545';
    return '#007bff';
  }};
  color: white;
  cursor: ${props => props.loading ? 'wait' : 'pointer'};
  transition: all 0.3s ease;
  
  ${props => props.loading && `
    &::after {
      content: '';
      position: absolute;
      right: 8px;
      top: 50%;
      transform: translateY(-50%);
      width: 16px;
      height: 16px;
      border: 2px solid transparent;
      border-top: 2px solid white;
      border-radius: 50%;
      animation: ${spin} 1s linear infinite;
    }
  `}
`;

export default StatusButton;

動的スタイルのテストコードです。

typescript// StatusButton.test.tsx
import { render, screen } from '@testing-library/react';
import StatusButton from './StatusButton';

describe('StatusButton コンポーネント', () => {
  test('ローディング状態のスタイルと動作', () => {
    render(<StatusButton loading>読み込み中</StatusButton>);
    
    const button = screen.getByRole('button');
    
    expect(button).toHaveStyleRule('background-color', '#ffc107');
    expect(button).toHaveStyleRule('cursor', 'wait');
    
    // ローディングアニメーションの確認
    expect(button).toHaveStyleRule('animation', 'css-spin 1s linear infinite', {
      target: '::after'
    });
  });

  test('成功・エラー状態のスタイル', () => {
    const { rerender } = render(<StatusButton success>成功</StatusButton>);
    let button = screen.getByRole('button');
    expect(button).toHaveStyleRule('background-color', '#28a745');

    rerender(<StatusButton error>エラー</StatusButton>);
    button = screen.getByRole('button');
    expect(button).toHaveStyleRule('background-color', '#dc3545');
  });

  test('状態の優先順位が正しく動作する', () => {
    // loading が他の状態より優先されることを確認
    render(<StatusButton loading success error>複数状態</StatusButton>);
    
    const button = screen.getByRole('button');
    expect(button).toHaveStyleRule('background-color', '#ffc107');
  });
});

テーマ切り替え機能のテスト

Emotion のテーマ機能を使った場合のテスト方法をご紹介します。

typescript// theme.ts
export const lightTheme = {
  colors: {
    primary: '#007bff',
    secondary: '#6c757d',
    background: '#ffffff',
    text: '#212529'
  },
  fonts: {
    body: 'Inter, sans-serif',
    heading: 'Poppins, sans-serif'
  }
};

export const darkTheme = {
  colors: {
    primary: '#0d6efd',
    secondary: '#6c757d',
    background: '#212529',
    text: '#ffffff'
  },
  fonts: {
    body: 'Inter, sans-serif',
    heading: 'Poppins, sans-serif'
  }
};

テーマを利用するコンポーネントです。

typescript// ThemedCard.tsx
import styled from '@emotion/styled';

const Card = styled.div`
  background-color: ${props => props.theme.colors.background};
  color: ${props => props.theme.colors.text};
  padding: 24px;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  font-family: ${props => props.theme.fonts.body};
`;

const CardTitle = styled.h2`
  color: ${props => props.theme.colors.primary};
  font-family: ${props => props.theme.fonts.heading};
  margin: 0 0 16px 0;
  font-size: 1.5rem;
`;

export { Card, CardTitle };

テーマ切り替えのテストコードです。

typescript// ThemedCard.test.tsx
import { render, screen } from '@testing-library/react';
import { ThemeProvider } from '@emotion/react';
import { Card, CardTitle } from './ThemedCard';
import { lightTheme, darkTheme } from './theme';

const renderWithTheme = (ui: React.ReactElement, theme = lightTheme) => {
  return render(
    <ThemeProvider theme={theme}>
      {ui}
    </ThemeProvider>
  );
};

describe('ThemedCard コンポーネント', () => {
  test('ライトテーマのスタイルが適用される', () => {
    renderWithTheme(
      <Card>
        <CardTitle>テストタイトル</CardTitle>
        <p>テスト内容</p>
      </Card>
    );
    
    const card = screen.getByText('テスト内容').parentElement;
    const title = screen.getByText('テストタイトル');
    
    expect(card).toHaveStyleRule('background-color', lightTheme.colors.background);
    expect(card).toHaveStyleRule('color', lightTheme.colors.text);
    expect(title).toHaveStyleRule('color', lightTheme.colors.primary);
  });

  test('ダークテーマのスタイルが適用される', () => {
    renderWithTheme(
      <Card>
        <CardTitle>テストタイトル</CardTitle>
        <p>テスト内容</p>
      </Card>,
      darkTheme
    );
    
    const card = screen.getByText('テスト内容').parentElement;
    const title = screen.getByText('テストタイトル');
    
    expect(card).toHaveStyleRule('background-color', darkTheme.colors.background);
    expect(card).toHaveStyleRule('color', darkTheme.colors.text);
    expect(title).toHaveStyleRule('color', darkTheme.colors.primary);
  });
});

レスポンシブデザインのテスト

メディアクエリを含むレスポンシブデザインのテストは、やや複雑になります。以下のアプローチをご参考ください。

typescript// ResponsiveGrid.tsx
import styled from '@emotion/styled';

const breakpoints = {
  sm: '576px',
  md: '768px',
  lg: '992px',
  xl: '1200px'
};

const Grid = styled.div`
  display: grid;
  gap: 16px;
  grid-template-columns: 1fr;
  
  @media (min-width: ${breakpoints.sm}) {
    grid-template-columns: repeat(2, 1fr);
  }
  
  @media (min-width: ${breakpoints.md}) {
    grid-template-columns: repeat(3, 1fr);
    gap: 24px;
  }
  
  @media (min-width: ${breakpoints.lg}) {
    grid-template-columns: repeat(4, 1fr);
    gap: 32px;
  }
`;

export default Grid;

レスポンシブデザインのテストでは、メディアクエリの動作を直接検証します。

typescript// ResponsiveGrid.test.tsx
import { render } from '@testing-library/react';
import Grid from './ResponsiveGrid';

// メディアクエリをモックする helper 関数
const mockMatchMedia = (query: string) => {
  Object.defineProperty(window, 'matchMedia', {
    writable: true,
    value: jest.fn().mockImplementation((q) => ({
      matches: q === query,
      media: q,
      onchange: null,
      addListener: jest.fn(),
      removeListener: jest.fn(),
      addEventListener: jest.fn(),
      removeEventListener: jest.fn(),
      dispatchEvent: jest.fn(),
    })),
  });
};

describe('ResponsiveGrid コンポーネント', () => {
  test('デスクトップサイズでのグリッドレイアウト', () => {
    mockMatchMedia('(min-width: 992px)');
    
    const { container } = render(
      <Grid>
        <div>アイテム1</div>
        <div>アイテム2</div>
        <div>アイテム3</div>
        <div>アイテム4</div>
      </Grid>
    );
    
    const grid = container.firstChild as HTMLElement;
    expect(grid).toHaveStyleRule('grid-template-columns', 'repeat(4, 1fr)', {
      media: '(min-width: 992px)'
    });
    expect(grid).toHaveStyleRule('gap', '32px', {
      media: '(min-width: 992px)'
    });
  });

  test('タブレットサイズでのグリッドレイアウト', () => {
    mockMatchMedia('(min-width: 768px)');
    
    const { container } = render(
      <Grid>
        <div>アイテム1</div>
        <div>アイテム2</div>
        <div>アイテム3</div>
      </Grid>
    );
    
    const grid = container.firstChild as HTMLElement;
    expect(grid).toHaveStyleRule('grid-template-columns', 'repeat(3, 1fr)', {
      media: '(min-width: 768px)'
    });
  });
});

以下の図は、レスポンシブテストの実行フローを示しています。

mermaidstateDiagram-v2
  [*] --> SetupTest: テスト開始
  SetupTest --> MockMediaQuery: メディアクエリモック設定
  MockMediaQuery --> RenderComponent: コンポーネントレンダリング
  RenderComponent --> CheckMobileStyles: モバイルスタイル検証
  CheckMobileStyles --> UpdateMediaQuery: メディアクエリ変更
  UpdateMediaQuery --> CheckTabletStyles: タブレットスタイル検証
  CheckTabletStyles --> CheckDesktopStyles: デスクトップスタイル検証
  CheckDesktopStyles --> [*]: テスト完了

図で理解できる要点:

  • メディアクエリのモック設定により各ブレークポイントをテスト
  • 段階的にスクリーンサイズを変更してスタイルを検証
  • レスポンシブデザインの各段階で期待値と実際のスタイルを比較

これらの具体例を参考に、皆様のプロジェクトに適したテスト戦略を構築してください。次に、これらの知見をまとめたベストプラクティスをご紹介します。

まとめ

ベストプラクティスの整理

Emotion と Jest・Testing Library を組み合わせた UI テストを効果的に行うために、以下のベストプラクティスを整理いたします。

環境設定のポイント

  1. 適切な依存関係の管理: @emotion​/​jestjest-environment-jsdom を必ずインストールし、スナップショットシリアライザーを設定する
  2. テーマプロバイダーの統一: テストユーティリティとしてテーマプロバイダー付きの render 関数を作成し、一貫性を保つ
  3. TypeScript 環境での型安全性: Emotion の props インターフェースを適切に定義し、テストコードでも型チェックを活用する

テスト戦略の選択指針

以下の表を参考に、テストケースに応じた適切な手法を選択してください。

テスト対象推奨手法理由
静的スタイルtoHaveStyleRule直接的で読みやすい
動的スタイルprops パターンテスト全パターンの網羅性
テーマ切り替えテーマプロバイダー切り替え実際の使用シーンに近い
レスポンシブメディアクエリモックブレークポイント検証
アニメーションキーフレーム存在確認パフォーマンス重視

コードの保守性を高める工夫

typescript// 再利用可能なテストユーティリティの例
export const createStyledComponentTest = <T extends object>(
  Component: React.ComponentType<T>,
  defaultProps: T
) => {
  return {
    renderWithProps: (props: Partial<T> = {}) => {
      return render(<Component {...defaultProps} {...props} />);
    },
    
    testStyleRule: (rule: string, expected: string, props: Partial<T> = {}) => {
      const { container } = render(<Component {...defaultProps} {...props} />);
      expect(container.firstChild).toHaveStyleRule(rule, expected);
    }
  };
};

パフォーマンス最適化のアプローチ

  • テストの並列実行: 関連性のないテストケースは並列実行を活用
  • スナップショットテストの併用: スタイルの大幅な変更検知にはスナップショットテストも有効
  • モックの適切な使用: 重い依存関係はモック化してテスト実行時間を短縮

運用時の注意点

実際の開発現場でこれらのテスト手法を運用する際の注意点をまとめました。

チーム開発での統一性確保

  • コードレビューでのチェックポイント: スタイルテストの網羅性と適切性を確認
  • テストコードの命名規則: 「何をテストしているか」が明確になる命名を徹底
  • 共通ユーティリティの活用: チーム内でテストユーティリティを共有し、一貫性を保つ

CI/CD パイプラインでの考慮事項

yaml# GitHub Actions の例
name: UI Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'yarn'
      
      - run: yarn install --frozen-lockfile
      - run: yarn test --coverage --watchAll=false
      - run: yarn test:visual # ビジュアルリグレッションテスト
      
      # テストカバレッジの確認
      - name: Coverage check
        run: |
          if [ $(yarn test --coverage --passWithNoTests --watchAll=false --coverageReporters=text-summary | grep "Statements" | awk '{print $3}' | sed 's/%//') -lt 80 ]; then
            echo "Coverage is below 80%"
            exit 1
          fi

デバッグとトラブルシューティング

よくある問題とその対処法をご紹介します。

typescript// スタイルが適用されない場合のデバッグ手法
test('スタイルデバッグの例', () => {
  const { container } = render(<StyledComponent />);
  const element = container.firstChild as HTMLElement;
  
  // 実際に適用されているスタイルを確認
  console.log('Applied styles:', window.getComputedStyle(element));
  
  // Emotion が生成したクラス名を確認  
  console.log('Class names:', element.className);
  
  // DOM 構造を確認
  console.log('DOM structure:', container.innerHTML);
});

パフォーマンスモニタリング

テスト実行時間の監視と最適化も重要です。

javascript// テスト実行時間の測定
const startTime = performance.now();

test('パフォーマンステスト', async () => {
  // テスト実行
  await render(<ComplexComponent />);
  
  const endTime = performance.now();
  const executionTime = endTime - startTime;
  
  // 1秒以上かかった場合は警告
  if (executionTime > 1000) {
    console.warn(`Slow test detected: ${executionTime}ms`);
  }
});

これらのベストプラクティスと注意点を参考に、皆様のプロジェクトでも効果的な UI テスト環境を構築していただければと思います。Emotion と Jest・Testing Library を組み合わせることで、品質の高い UI コンポーネントの開発と保守が可能になるでしょう。

関連リンク

公式ドキュメント

テスト関連ツール

開発環境・設定関連

参考記事・チュートリアル

コミュニティ・サポート