はじめに
マイクロ フロントエンドのテストには、通常の SPA テストと比較して追加の課題があります。各 MFE は、分離 (スタンドアロン) および 統合 (シェル アプリ内) をテストする必要があります。この記事では、両方のシナリオのテスト戦略を構築します。
1. マイクロフロントエンドのテストレベル
┌─────────────────────────────────────┐
│ E2E Tests (Playwright/Cypress) │ Full app, all MFEs loaded
│ Critical user flows across MFEs │
├─────────────────────────────────────┤
│ Integration Tests │ MFE loaded in Shell
│ Cross-MFE communication, routing │
├─────────────────────────────────────┤
│ Visual Regression Tests │ Screenshot comparison
│ Chromatic / Percy / Playwright │
├─────────────────────────────────────┤
│ Component Tests (RTL) │ Individual components
│ Each MFE tested in isolation │
├─────────────────────────────────────┤
│ Unit Tests (Jest/Vitest) │ Hooks, utils, business logic
│ Pure functions, state management │
└─────────────────────────────────────┘
2. コンポーネントのテスト (React テスト ライブラリ)
// ProductCard.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { ProductCard } from './ProductCard';
describe('ProductCard', () => {
const product = {
id: '1', name: 'Laptop', price: 999, image: '/laptop.jpg'
};
it('renders product info', () => {
render(<ProductCard product={product} />);
expect(screen.getByText('Laptop')).toBeInTheDocument();
expect(screen.getByText('$999')).toBeInTheDocument();
});
it('dispatches add-to-cart event', () => {
const spy = jest.fn();
window.addEventListener('cart:item-added', spy);
render(<ProductCard product={product} />);
fireEvent.click(screen.getByRole('button', { name: /add to cart/i }));
expect(spy).toHaveBeenCalled();
window.removeEventListener('cart:item-added', spy);
});
});
3. 視覚的な回帰テスト
3.1 ストーリーブック + クロマチック
// Button.stories.tsx
export const AllVariants = () => (
<div>
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="danger">Danger</Button>
<Button disabled>Disabled</Button>
</div>
);
クロマチックワークフロー:
1. Push code → CI runs Storybook build
2. Chromatic captures screenshots
3. Compare with baseline
4. Visual diff review → Approve/Reject
3.2 劇作家のビジュアル
test('product list matches snapshot', async ({ page }) => {
await page.goto('/products');
await expect(page).toHaveScreenshot('product-list.png', {
maxDiffPixelRatio: 0.01,
});
});
4. 統合テスト (シェル内の MFE)
// Test Product MFE loaded in Shell context
describe('Product MFE Integration', () => {
it('loads product list in shell', async ({ page }) => {
await page.goto('/products');
// Verify MFE loaded successfully
await expect(page.getByTestId('product-list')).toBeVisible();
// Verify routing works
await page.click('[data-testid="product-card"]');
await expect(page).toHaveURL(/\/products\/\d+/);
});
it('add to cart updates cart badge in shell header', async ({ page }) => {
await page.goto('/products');
// Click add to cart (Product MFE)
await page.click('[data-testid="add-to-cart"]');
// Verify cart badge updated (Shell Header)
await expect(page.getByTestId('cart-badge')).toHaveText('1');
});
});
5. MFE ごとのテスト戦略
| テストの種類 | いつ | ツール | CIステージ |
|---|---|---|---|
| ユニット | すべてのコミット | ジェスト/ヴィテスト | マージ前 |
| コンポーネント | すべてのコミット | RTL | マージ前 |
| ビジュアル | あらゆるPR | クロマチック | PRレビュー |
| 統合 | 毎日/毎週 | 劇作家 | マージ後 |
| E2E | 毎日 | 劇作家 | ステージング |
6. ベストプラクティス
✅ Test MFE standalone (without Shell) → fast feedback
✅ Test MFE in Shell → integration correctness
✅ Visual regression cho Design System → catch UI drift
✅ Test cross-MFE events → verify contracts
✅ Mock API calls in component tests → deterministic
❌ Don't test implementation details
❌ Don't test remote MFE code from another MFE's test suite
❌ Don't rely on E2E for all coverage → too slow
概要
- コンポーネント テスト: RTL を使用したスタンドアロンの各 MFE テスト
- ビジュアル回帰: ストーリーブック + クロマティック キャッチ UI の変更
- 統合テスト: シェル内の MFE、クロス MFE 通信
- E2E: クリティカル フローのみ、ステージングで実行
- MFE 間の イベント (コントラクト) をテストする