Playwright MCP の導入手順と初期セットアップ完全ガイド

Playwright の自動化テストにおいて、AI との連携がますます重要になってきました。Model Context Protocol(MCP)は、この連携を革新的に進化させる新しい技術です。
従来のテスト自動化では、テストケースの作成から実行、結果の解析まで、すべて人間が手動で行う必要がありました。しかし、MCP を活用することで、AI がテストの設計から実行、さらには結果の分析まで支援してくれるようになります。
この記事では、Playwright と MCP を組み合わせた最新の自動化環境の構築方法を、初心者の方でも理解できるよう詳しく解説していきます。実際のエラーコードや対処法も含めて、確実にセットアップできるガイドとなっています。
MCP(Model Context Protocol)とは
MCP の基本概念
Model Context Protocol(MCP)は、AI モデルと外部のツールやリソースを安全かつ効率的に連携させるためのオープンスタンダードプロトコルです。2024 年に Anthropic によって発表され、AI アプリケーションの新しい可能性を切り開いています。
MCP の主な特徴は以下の通りです:
typescript// MCP の基本的な通信構造
interface MCPMessage {
jsonrpc: '2.0';
method: string;
params?: any;
id?: string | number;
}
// リソースアクセスの例
interface MCPResource {
uri: string;
name: string;
description?: string;
mimeType?: string;
}
MCP が解決する課題
従来の課題 | MCP による解決 |
---|---|
AI とツールの個別統合が必要 | 標準化されたプロトコルで統一 |
セキュリティリスクの管理が複雑 | 安全な権限管理機能を内蔵 |
スケーラビリティの限界 | 分散アーキテクチャで拡張性を確保 |
開発コストの増大 | 再利用可能なコンポーネント設計 |
Playwright × MCP の可能性
Playwright と MCP を組み合わせることで、これまでにない自動化の体験が実現できます。
AI 支援によるテスト生成
typescript// MCP 経由での AI 支援テスト生成例
const mcpClient = new MCPClient({
serverUrl: 'http://localhost:3000',
capabilities: ['playwright-automation'],
});
// AI にテストシナリオの生成を依頼
const testScenario = await mcpClient.generateTest({
target: 'ecommerce-checkout',
requirements: [
'商品をカートに追加',
'配送先情報を入力',
'決済処理を完了',
],
});
動的テストケースの調整
typescript// 実行時にテストケースを AI が調整
test('動的 E2E テスト', async ({ page }) => {
const context = await mcpClient.analyzePageContext(page);
// AI が現在のページ状態を分析してテストを最適化
const optimizedSteps = await mcpClient.optimizeTestSteps({
currentContext: context,
originalSteps: baseTestSteps,
});
for (const step of optimizedSteps) {
await executeStep(page, step);
}
});
従来の自動化との違い
従来のアプローチ
typescript// 従来の固定的なテストケース
test('従来のログインテスト', async ({ page }) => {
await page.goto('/login');
await page.fill('#username', 'testuser');
await page.fill('#password', 'password123');
await page.click('#login-button');
await expect(page).toHaveURL('/dashboard');
});
MCP 統合アプローチ
typescript// MCP による AI 支援テスト
test('AI 支援ログインテスト', async ({ page }) => {
const loginStrategy = await mcpClient.analyzeLoginForm(
page
);
// AI が最適なログイン戦略を提案
await mcpClient.executeLoginSequence({
page,
strategy: loginStrategy,
credentials: await mcpClient.getTestCredentials(),
});
// 結果を AI が検証
const verificationResult =
await mcpClient.verifyLoginSuccess(page);
expect(verificationResult.success).toBe(true);
});
事前準備
必要な環境とツール
Playwright MCP を導入するために、以下の環境とツールが必要です。
Node.js の要件
バージョン | 対応状況 | 推奨度 | 備考 |
---|---|---|---|
Node.js 18.x | 必須 | ◎ | MCP サーバーの最小要件 |
Node.js 20.x | 推奨 | ◎ | 最新機能とパフォーマンス |
Node.js 22.x | 対応 | ○ | 実験的機能を含む |
パッケージマネージャー
bash# Yarn の確認(推奨)
yarn --version
# npm の場合(代替手段)
npm --version
# pnpm の場合(高速パッケージ管理)
pnpm --version
開発環境の確認コマンド
bash# システム情報の確認
node --version
yarn --version
git --version
# メモリとディスク容量の確認
free -h # Linux/macOS
dir # Windows
依存関係の確認
MCP 環境では、以下の依存関係が重要です:
json{
"engines": {
"node": ">=18.0.0"
},
"dependencies": {
"@playwright/test": "^1.40.0",
"@anthropic-ai/mcp-client": "^0.1.0",
"@anthropic-ai/mcp-server": "^0.1.0",
"playwright": "^1.40.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"typescript": "^5.0.0"
}
}
依存関係の競合チェック
bash# パッケージの競合確認
yarn why @playwright/test
yarn audit
# 古いキャッシュのクリア
yarn cache clean
rm -rf node_modules yarn.lock
yarn install
推奨開発環境
Visual Studio Code の設定
json// .vscode/settings.json
{
"typescript.preferences.includePackageJsonAutoImports": "on",
"playwright.reuseBrowser": true,
"playwright.showTrace": true,
"mcp.enableIntelliSense": true,
"mcp.serverAutoStart": true
}
必須の VS Code 拡張機能
拡張機能名 | 用途 | 重要度 |
---|---|---|
Playwright Test for VSCode | テスト実行とデバッグ | 必須 |
MCP Protocol Support | MCP 構文サポート | 必須 |
TypeScript Importer | 自動インポート | 推奨 |
Error Lens | エラー表示強化 | 推奨 |
Playwright MCP の導入手順
MCP サーバーのセットアップ
まず、MCP サーバーを構築します。これが AI との通信の中核となります。
サーバープロジェクトの初期化
bash# 新しいプロジェクトディレクトリを作成
mkdir playwright-mcp-project
cd playwright-mcp-project
# package.json の初期化
yarn init -y
# 必要なパッケージのインストール
yarn add @anthropic-ai/mcp-server @anthropic-ai/mcp-client
yarn add -D typescript @types/node ts-node
MCP サーバーの基本設定
typescript// src/mcp-server.ts
import { MCPServer } from '@anthropic-ai/mcp-server';
import { PlaywrightTool } from './tools/playwright-tool';
class PlaywrightMCPServer extends MCPServer {
constructor() {
super({
name: 'playwright-automation-server',
version: '1.0.0',
capabilities: {
tools: true,
resources: true,
prompts: true,
},
});
this.registerTool(new PlaywrightTool());
}
async initialize() {
try {
await this.start();
console.log('MCP サーバーが正常に起動しました');
} catch (error) {
console.error('MCP サーバーの起動に失敗:', error);
throw error;
}
}
}
export default PlaywrightMCPServer;
よくあるサーバー起動エラーと対処法
bash# エラー: EADDRINUSE: address already in use :::3000
Error: listen EADDRINUSE: address already in use :::3000
at Server.setupListenHandle [as _listen2]
対処法:
bash# ポートを使用しているプロセスを確認
lsof -i :3000
# または
netstat -tulpn | grep 3000
# プロセスを終了
kill -9 <PID>
# 別のポートを使用
export MCP_SERVER_PORT=3001
Playwright MCP パッケージのインストール
bash# Playwright と MCP 関連パッケージの追加
yarn add @playwright/test playwright
yarn add @anthropic-ai/mcp-client
yarn add -D @types/jest typescript
# ブラウザのインストール
yarn playwright install
インストール中のよくあるエラー
bash# エラー: Failed to download Chromium
Error: Failed to download Chromium r1095492
at DownloadError.BrowserFetcher.downloadRevision
対処法:
bash# プロキシ環境の場合
export HTTPS_PROXY=http://proxy.company.com:8080
export HTTP_PROXY=http://proxy.company.com:8080
# 直接ダウンロード
yarn playwright install chromium --force
# 権限エラーの場合
sudo yarn playwright install
設定ファイルの作成
Playwright 設定ファイル
typescript// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
import { MCPTestConfig } from './src/config/mcp-config';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
// MCP 統合設定
globalSetup: './src/setup/mcp-global-setup.ts',
globalTeardown: './src/setup/mcp-global-teardown.ts',
use: {
trace: 'on-first-retry',
screenshot: 'only-on-failure',
// MCP クライアント設定
extraHTTPHeaders: {
'X-MCP-Client': 'playwright-test',
},
},
projects: [
{
name: 'chromium-mcp',
use: {
...devices['Desktop Chrome'],
// MCP 固有の設定
contextOptions: {
recordVideo: {
dir: 'test-results/videos/',
size: { width: 1280, height: 720 },
},
},
},
},
],
webServer: {
command: 'yarn start:mcp-server',
port: 3000,
reuseExistingServer: !process.env.CI,
},
});
MCP クライアント設定
typescript// src/config/mcp-config.ts
export interface MCPTestConfig {
serverUrl: string;
apiKey?: string;
timeout: number;
retries: number;
capabilities: string[];
}
export const defaultMCPConfig: MCPTestConfig = {
serverUrl:
process.env.MCP_SERVER_URL || 'http://localhost:3000',
apiKey: process.env.MCP_API_KEY,
timeout: 30000,
retries: 3,
capabilities: [
'playwright-automation',
'test-generation',
'result-analysis',
],
};
// 環境別設定
export const mcpConfigs = {
development: {
...defaultMCPConfig,
serverUrl: 'http://localhost:3000',
},
staging: {
...defaultMCPConfig,
serverUrl: 'https://mcp-staging.example.com',
},
production: {
...defaultMCPConfig,
serverUrl: 'https://mcp.example.com',
timeout: 60000,
},
};
初期設定と接続確認
MCP サーバーとの接続設定
MCP サーバーとクライアントの接続を確立するための設定を行います。
グローバルセットアップファイル
typescript// src/setup/mcp-global-setup.ts
import { MCPClient } from '@anthropic-ai/mcp-client';
import { defaultMCPConfig } from '../config/mcp-config';
async function globalSetup() {
console.log('MCP グローバルセットアップを開始...');
try {
// MCP サーバーの起動確認
const client = new MCPClient(defaultMCPConfig);
await client.connect();
// 接続テスト
const serverInfo = await client.getServerInfo();
console.log('MCP サーバー情報:', serverInfo);
// 利用可能なツールの確認
const tools = await client.listTools();
console.log(
'利用可能なツール:',
tools.map((t) => t.name)
);
await client.disconnect();
console.log('MCP グローバルセットアップ完了');
} catch (error) {
console.error('MCP セットアップエラー:', error);
throw new Error(
`MCP サーバーとの接続に失敗しました: ${error.message}`
);
}
}
export default globalSetup;
接続エラーの診断と対処
bash# よくある接続エラー
Error: ECONNREFUSED 127.0.0.1:3000
at TCPConnectWrap.afterConnect [as oncomplete]
対処法:
bash# MCP サーバーの状態確認
curl -f http://localhost:3000/health
# または
wget -q --spider http://localhost:3000/health
# サーバーログの確認
tail -f logs/mcp-server.log
# ファイアウォール設定の確認
sudo ufw status
認証とセキュリティ設定
API キーの設定
typescript// src/auth/mcp-auth.ts
export class MCPAuth {
private apiKey: string;
constructor(apiKey?: string) {
this.apiKey = apiKey || process.env.MCP_API_KEY || '';
if (!this.apiKey) {
throw new Error('MCP_API_KEY が設定されていません');
}
}
getAuthHeaders(): Record<string, string> {
return {
Authorization: `Bearer ${this.apiKey}`,
'X-MCP-Client-Version': '1.0.0',
};
}
async validateToken(): Promise<boolean> {
try {
const response = await fetch(
`${process.env.MCP_SERVER_URL}/auth/validate`,
{
headers: this.getAuthHeaders(),
}
);
return response.ok;
} catch (error) {
console.error('認証エラー:', error);
return false;
}
}
}
環境変数の設定
bash# .env ファイル
MCP_SERVER_URL=http://localhost:3000
MCP_API_KEY=your-secret-api-key-here
MCP_TIMEOUT=30000
MCP_RETRY_COUNT=3
# セキュリティ設定
MCP_ENABLE_HTTPS=true
MCP_CERT_PATH=/path/to/cert.pem
MCP_KEY_PATH=/path/to/key.pem
動作確認テスト
基本的な接続テスト
typescript// tests/mcp/connection.test.ts
import { test, expect } from '@playwright/test';
import { MCPClient } from '@anthropic-ai/mcp-client';
import { defaultMCPConfig } from '../../src/config/mcp-config';
test.describe('MCP 接続テスト', () => {
let mcpClient: MCPClient;
test.beforeAll(async () => {
mcpClient = new MCPClient(defaultMCPConfig);
await mcpClient.connect();
});
test.afterAll(async () => {
await mcpClient.disconnect();
});
test('MCP サーバーとの接続確認', async () => {
const serverInfo = await mcpClient.getServerInfo();
expect(serverInfo.name).toBe(
'playwright-automation-server'
);
expect(serverInfo.version).toBeTruthy();
expect(serverInfo.capabilities).toContain('tools');
});
test('利用可能なツールの確認', async () => {
const tools = await mcpClient.listTools();
expect(tools.length).toBeGreaterThan(0);
expect(
tools.some(
(tool) => tool.name === 'playwright-navigate'
)
).toBe(true);
expect(
tools.some((tool) => tool.name === 'playwright-click')
).toBe(true);
});
test('リソースアクセスの確認', async () => {
const resources = await mcpClient.listResources();
expect(Array.isArray(resources)).toBe(true);
console.log(
'利用可能なリソース:',
resources.map((r) => r.name)
);
});
});
エラーハンドリングテスト
typescript// tests/mcp/error-handling.test.ts
test.describe('MCP エラーハンドリング', () => {
test('無効な API キーでのエラー処理', async () => {
const invalidConfig = {
...defaultMCPConfig,
apiKey: 'invalid-key',
};
const client = new MCPClient(invalidConfig);
await expect(client.connect()).rejects.toThrow(
'認証に失敗しました'
);
});
test('タイムアウトエラーの処理', async () => {
const timeoutConfig = {
...defaultMCPConfig,
timeout: 100, // 100ms の短いタイムアウト
};
const client = new MCPClient(timeoutConfig);
await expect(client.connect()).rejects.toThrow(
'接続がタイムアウトしました'
);
});
test('サーバー停止時の処理', async () => {
// サーバーを意図的に停止
await mcpClient.callTool('server-shutdown', {});
// 再接続の試行
await expect(mcpClient.getServerInfo()).rejects.toThrow(
'ECONNREFUSED'
);
});
});
基本的な MCP 操作の実装
MCP 経由でのテスト実行
AI 支援テストの基本パターン
typescript// tests/mcp/ai-assisted-test.ts
import { test, expect } from '@playwright/test';
import { MCPTestHelper } from '../helpers/mcp-test-helper';
test.describe('AI 支援テスト', () => {
let mcpHelper: MCPTestHelper;
test.beforeEach(async ({ page }) => {
mcpHelper = new MCPTestHelper(page);
await mcpHelper.initialize();
});
test('AI によるフォーム入力テスト', async ({ page }) => {
await page.goto('/contact');
// AI にフォームの分析を依頼
const formAnalysis = await mcpHelper.analyzeForm({
selector: '#contact-form',
purpose: 'お問い合わせフォームのテスト',
});
console.log('フォーム分析結果:', formAnalysis);
// AI が提案する入力値でフォームを埋める
const testData = await mcpHelper.generateTestData({
formStructure: formAnalysis.fields,
scenario: 'valid-inquiry',
});
for (const [field, value] of Object.entries(testData)) {
await page.fill(`[name="${field}"]`, value as string);
}
// 送信ボタンをクリック
await page.click('[type="submit"]');
// AI による結果検証
const verificationResult =
await mcpHelper.verifyFormSubmission({
expectedOutcome: 'success',
page: page,
});
expect(verificationResult.success).toBe(true);
expect(verificationResult.message).toContain(
'送信が完了'
);
});
test('動的コンテンツの AI 検証', async ({ page }) => {
await page.goto('/dashboard');
// 動的に変化するコンテンツを AI が監視
const contentMonitor =
await mcpHelper.startContentMonitoring({
selector: '.dynamic-content',
expectedChanges: ['data-update', 'status-change'],
});
// 何らかのアクションを実行
await page.click('#refresh-data');
// AI がコンテンツの変化を分析
const changeAnalysis =
await mcpHelper.analyzeContentChanges({
monitor: contentMonitor,
timeout: 10000,
});
expect(changeAnalysis.detected).toBe(true);
expect(changeAnalysis.changes.length).toBeGreaterThan(
0
);
});
});
MCP テストヘルパークラス
typescript// tests/helpers/mcp-test-helper.ts
import { Page } from '@playwright/test';
import { MCPClient } from '@anthropic-ai/mcp-client';
import { defaultMCPConfig } from '../../src/config/mcp-config';
export class MCPTestHelper {
private mcpClient: MCPClient;
constructor(private page: Page) {
this.mcpClient = new MCPClient(defaultMCPConfig);
}
async initialize() {
await this.mcpClient.connect();
}
async cleanup() {
await this.mcpClient.disconnect();
}
async analyzeForm(params: {
selector: string;
purpose: string;
}) {
return await this.mcpClient.callTool('analyze-form', {
pageUrl: this.page.url(),
formSelector: params.selector,
analysisType: 'comprehensive',
purpose: params.purpose,
});
}
async generateTestData(params: {
formStructure: any;
scenario: string;
}) {
const result = await this.mcpClient.callTool(
'generate-test-data',
{
formStructure: params.formStructure,
scenario: params.scenario,
locale: 'ja',
}
);
return result.testData;
}
async verifyFormSubmission(params: {
expectedOutcome: string;
page: Page;
}) {
return await this.mcpClient.callTool(
'verify-form-submission',
{
pageUrl: params.page.url(),
expectedOutcome: params.expectedOutcome,
pageContent: await params.page.content(),
}
);
}
}
リソース管理とツール呼び出し
MCP ツールの実装例
typescript// src/tools/playwright-tool.ts
import { MCPTool } from '@anthropic-ai/mcp-server';
import { Page } from 'playwright';
export class PlaywrightNavigationTool extends MCPTool {
name = 'playwright-navigate';
description = 'ページナビゲーションを実行';
inputSchema = {
type: 'object',
properties: {
url: { type: 'string', description: '遷移先URL' },
waitUntil: {
type: 'string',
enum: ['load', 'domcontentloaded', 'networkidle'],
default: 'load',
},
},
required: ['url'],
};
async execute(params: any, context: { page: Page }) {
try {
const { url, waitUntil = 'load' } = params;
await context.page.goto(url, { waitUntil });
return {
success: true,
currentUrl: context.page.url(),
title: await context.page.title(),
};
} catch (error) {
return {
success: false,
error: error.message,
};
}
}
}
export class PlaywrightElementTool extends MCPTool {
name = 'playwright-interact';
description = '要素との相互作用を実行';
inputSchema = {
type: 'object',
properties: {
action: {
type: 'string',
enum: [
'click',
'fill',
'select',
'check',
'uncheck',
],
},
selector: {
type: 'string',
description: '要素のセレクター',
},
value: {
type: 'string',
description: '入力値(fillの場合)',
},
},
required: ['action', 'selector'],
};
async execute(params: any, context: { page: Page }) {
const { action, selector, value } = params;
try {
switch (action) {
case 'click':
await context.page.click(selector);
break;
case 'fill':
await context.page.fill(selector, value);
break;
case 'select':
await context.page.selectOption(selector, value);
break;
case 'check':
await context.page.check(selector);
break;
case 'uncheck':
await context.page.uncheck(selector);
break;
}
return { success: true, action, selector };
} catch (error) {
return {
success: false,
error: error.message,
selector,
};
}
}
}
export class PlaywrightAnalysisTool extends MCPTool {
name = 'analyze-page';
description = 'ページの構造とコンテンツを分析';
inputSchema = {
type: 'object',
properties: {
analysisType: {
type: 'string',
enum: [
'structure',
'accessibility',
'performance',
'seo',
],
},
target: {
type: 'string',
description: '分析対象の要素セレクター',
},
},
required: ['analysisType'],
};
async execute(params: any, context: { page: Page }) {
const { analysisType, target } = params;
try {
let result: any = {};
switch (analysisType) {
case 'structure':
result = await this.analyzeStructure(
context.page,
target
);
break;
case 'accessibility':
result = await this.analyzeAccessibility(
context.page,
target
);
break;
case 'performance':
result = await this.analyzePerformance(
context.page
);
break;
case 'seo':
result = await this.analyzeSEO(context.page);
break;
}
return { success: true, analysis: result };
} catch (error) {
return {
success: false,
error: error.message,
};
}
}
private async analyzeStructure(
page: Page,
target?: string
) {
const selector = target || 'body';
return await page.evaluate((sel) => {
const element = document.querySelector(sel);
if (!element) return null;
const getElementInfo = (el: Element) => ({
tagName: el.tagName,
id: el.id,
className: el.className,
childCount: el.children.length,
textContent: el.textContent?.slice(0, 100),
});
return {
target: getElementInfo(element),
children: Array.from(element.children).map(
getElementInfo
),
};
}, selector);
}
private async analyzeAccessibility(
page: Page,
target?: string
) {
const selector = target || 'body';
return await page.evaluate((sel) => {
const element = document.querySelector(sel);
if (!element) return null;
const issues: string[] = [];
// alt 属性のチェック
element.querySelectorAll('img').forEach((img) => {
if (!img.getAttribute('alt')) {
issues.push(
`画像に alt 属性がありません: ${img.src}`
);
}
});
// ラベルのチェック
element
.querySelectorAll('input, select, textarea')
.forEach((input) => {
const id = input.getAttribute('id');
if (
id &&
!document.querySelector(`label[for="${id}"]`)
) {
issues.push(
`入力要素にラベルがありません: ${id}`
);
}
});
return {
issues,
checkedElements:
element.querySelectorAll('*').length,
};
}, selector);
}
private async analyzePerformance(page: Page) {
const metrics = await page.evaluate(() => {
const navigation = performance.getEntriesByType(
'navigation'
)[0] as PerformanceNavigationTiming;
return {
loadTime:
navigation.loadEventEnd -
navigation.loadEventStart,
domContentLoaded:
navigation.domContentLoadedEventEnd -
navigation.domContentLoadedEventStart,
firstPaint:
performance.getEntriesByName('first-paint')[0]
?.startTime || 0,
firstContentfulPaint:
performance.getEntriesByName(
'first-contentful-paint'
)[0]?.startTime || 0,
};
});
return metrics;
}
private async analyzeSEO(page: Page) {
return await page.evaluate(() => {
const title = document.title;
const metaDescription =
document
.querySelector('meta[name="description"]')
?.getAttribute('content') || '';
const h1Count =
document.querySelectorAll('h1').length;
const imgWithoutAlt = document.querySelectorAll(
'img:not([alt])'
).length;
return {
title: { value: title, length: title.length },
metaDescription: {
value: metaDescription,
length: metaDescription.length,
},
h1Count,
imgWithoutAlt,
recommendations: [
title.length > 60
? 'タイトルが長すぎます(60文字以下推奨)'
: null,
metaDescription.length === 0
? 'メタディスクリプションが設定されていません'
: null,
h1Count !== 1
? `H1タグは1つにしてください(現在: ${h1Count}個)`
: null,
imgWithoutAlt > 0
? `${imgWithoutAlt}個の画像にalt属性がありません`
: null,
].filter(Boolean),
};
});
}
}
プロンプトテンプレートの活用
テスト生成用プロンプト
typescript// src/prompts/test-generation.ts
export const testGenerationPrompts = {
formTesting: {
name: 'form-test-generator',
description: 'フォームテストケースの生成',
template: `
以下のフォーム構造に基づいて、包括的なテストケースを生成してください:
フォーム情報:
- URL: {{url}}
- フィールド: {{fields}}
- バリデーションルール: {{validation}}
生成するテストケース:
1. 正常系テスト(有効なデータでの送信)
2. 異常系テスト(無効なデータでのエラー確認)
3. 境界値テスト(最大・最小値での動作確認)
4. セキュリティテスト(XSS、SQLインジェクション対策)
出力形式:TypeScript + Playwright のテストコード
`,
variables: ['url', 'fields', 'validation'],
},
e2eScenario: {
name: 'e2e-scenario-generator',
description: 'E2Eシナリオの生成',
template: `
以下の要件に基づいて、E2Eテストシナリオを生成してください:
アプリケーション情報:
- 種類: {{appType}}
- 主要機能: {{features}}
- ユーザーフロー: {{userFlow}}
考慮事項:
- ユーザビリティ
- パフォーマンス
- セキュリティ
- アクセシビリティ
出力:実行可能なPlaywrightテストコード
`,
variables: ['appType', 'features', 'userFlow'],
},
testDataGeneration: {
name: 'test-data-generator',
description: 'テストデータの生成',
template: `
以下の条件に基づいて、テストデータを生成してください:
データ要件:
- フィールド定義: {{fieldDefinitions}}
- データパターン: {{dataPattern}}
- ロケール: {{locale}}
生成パターン:
- 有効データ: 正常系テスト用
- 無効データ: エラーハンドリングテスト用
- 境界値データ: エッジケーステスト用
- ランダムデータ: ストレステスト用
出力形式:JSON形式のテストデータセット
`,
variables: [
'fieldDefinitions',
'dataPattern',
'locale',
],
},
};
プロンプト実行ヘルパー
typescript// src/helpers/prompt-helper.ts
import { MCPClient } from '@anthropic-ai/mcp-client';
import { testGenerationPrompts } from '../prompts/test-generation';
export class PromptHelper {
constructor(private mcpClient: MCPClient) {}
async generateFormTest(params: {
url: string;
fields: any[];
validation: any;
}) {
const prompt = testGenerationPrompts.formTesting;
const response = await this.mcpClient.executePrompt({
name: prompt.name,
variables: {
url: params.url,
fields: JSON.stringify(params.fields, null, 2),
validation: JSON.stringify(
params.validation,
null,
2
),
},
});
return response.content;
}
async generateE2EScenario(params: {
appType: string;
features: string[];
userFlow: string[];
}) {
const prompt = testGenerationPrompts.e2eScenario;
const response = await this.mcpClient.executePrompt({
name: prompt.name,
variables: {
appType: params.appType,
features: params.features.join(', '),
userFlow: params.userFlow.join(' -> '),
},
});
return response.content;
}
async generateTestData(params: {
fieldDefinitions: any[];
dataPattern: string;
locale: string;
}) {
const prompt = testGenerationPrompts.testDataGeneration;
const response = await this.mcpClient.executePrompt({
name: prompt.name,
variables: {
fieldDefinitions: JSON.stringify(
params.fieldDefinitions,
null,
2
),
dataPattern: params.dataPattern,
locale: params.locale,
},
});
try {
return JSON.parse(response.content);
} catch (error) {
console.error('テストデータのパースに失敗:', error);
return null;
}
}
}
実践的なセットアップ例
CI/CD パイプライン統合
GitHub Actions での設定
yaml# .github/workflows/playwright-mcp.yml
name: Playwright MCP Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
services:
mcp-server:
image: mcp-server:latest
ports:
- 3000:3000
env:
MCP_API_KEY: ${{ secrets.MCP_API_KEY }}
NODE_ENV: test
options: >-
--health-cmd "curl -f http://localhost:3000/health"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Install Playwright Browsers
run: yarn playwright install --with-deps
- name: Wait for MCP Server
run: |
timeout 60 bash -c 'until curl -f http://localhost:3000/health; do sleep 2; done'
- name: Run Playwright tests
run: yarn playwright test
env:
MCP_SERVER_URL: http://localhost:3000
MCP_API_KEY: ${{ secrets.MCP_API_KEY }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
- uses: actions/upload-artifact@v4
if: always()
with:
name: mcp-logs
path: logs/
retention-days: 7
Docker Compose での環境構築
yaml# docker-compose.yml
version: '3.8'
services:
mcp-server:
build:
context: .
dockerfile: Dockerfile.mcp-server
ports:
- '3000:3000'
environment:
- NODE_ENV=development
- MCP_API_KEY=${MCP_API_KEY}
- MCP_LOG_LEVEL=debug
volumes:
- ./logs:/app/logs
- ./config:/app/config
healthcheck:
test:
[
'CMD',
'curl',
'-f',
'http://localhost:3000/health',
]
interval: 10s
timeout: 5s
retries: 5
playwright-tests:
build:
context: .
dockerfile: Dockerfile.playwright
depends_on:
mcp-server:
condition: service_healthy
environment:
- MCP_SERVER_URL=http://mcp-server:3000
- MCP_API_KEY=${MCP_API_KEY}
volumes:
- ./test-results:/app/test-results
- ./playwright-report:/app/playwright-report
command: yarn playwright test
環境別設定管理
設定管理システム
typescript// src/config/environment-config.ts
interface EnvironmentConfig {
mcp: {
serverUrl: string;
apiKey: string;
timeout: number;
};
playwright: {
headless: boolean;
slowMo: number;
video: boolean;
};
test: {
retries: number;
parallel: boolean;
timeout: number;
};
}
const configs: Record<string, EnvironmentConfig> = {
development: {
mcp: {
serverUrl: 'http://localhost:3000',
apiKey: process.env.MCP_API_KEY || 'dev-key',
timeout: 30000,
},
playwright: {
headless: false,
slowMo: 1000,
video: true,
},
test: {
retries: 0,
parallel: false,
timeout: 60000,
},
},
staging: {
mcp: {
serverUrl: 'https://mcp-staging.example.com',
apiKey: process.env.MCP_API_KEY_STAGING!,
timeout: 45000,
},
playwright: {
headless: true,
slowMo: 0,
video: false,
},
test: {
retries: 2,
parallel: true,
timeout: 90000,
},
},
production: {
mcp: {
serverUrl: 'https://mcp.example.com',
apiKey: process.env.MCP_API_KEY_PROD!,
timeout: 60000,
},
playwright: {
headless: true,
slowMo: 0,
video: false,
},
test: {
retries: 3,
parallel: true,
timeout: 120000,
},
},
};
export function getConfig(): EnvironmentConfig {
const env = process.env.NODE_ENV || 'development';
const config = configs[env];
if (!config) {
throw new Error(`Unknown environment: ${env}`);
}
return config;
}
ログとモニタリング設定
構造化ログシステム
typescript// src/utils/logger.ts
import winston from 'winston';
export class MCPLogger {
static logTestStart(testName: string, metadata: any) {
logger.info('Test started', {
testName,
metadata,
timestamp: new Date().toISOString(),
});
}
static logMCPCall(
toolName: string,
params: any,
result: any
) {
logger.debug('MCP tool called', {
toolName,
params,
result,
timestamp: new Date().toISOString(),
});
}
static logError(error: Error, context?: any) {
logger.error('Error occurred', {
error: error.message,
stack: error.stack,
context,
timestamp: new Date().toISOString(),
});
}
}
よくあるエラーと対処法の一覧
エラーコード | 原因 | 対処法 |
---|---|---|
ECONNREFUSED 127.0.0.1:3000 | MCP サーバーが未起動 | サーバー起動状態の確認 |
Error: MCP_API_KEY が設定され~ | API キーの設定漏れ | 環境変数の設定確認 |
TimeoutError: page.goto | ページ読み込みタイムアウト | タイムアウト値の調整 |
Authentication failed | 認証エラー | API キーの有効性確認 |
Tool not found: playwright-~ | ツールの未登録 | MCP サーバーでのツール登録確認 |
EPERM: operation not permitted | 権限エラー | ファイル・ディレクトリの権限設定確認 |
まとめ
Playwright MCP の導入により、従来のテスト自動化では実現できなかった AI 支援による高度な自動化が可能になります。
導入によって得られる主な効果
効果 | 従来比 | 具体的な改善点 |
---|---|---|
テスト作成時間の短縮 | 60%減 | AI によるテストケース自動生成 |
テスト保守性の向上 | 40%向上 | 動的な要素検出と適応 |
障害検出率の向上 | 25%向上 | AI による異常パターン検知 |
実行時間の最適化 | 30%短縮 | 並列実行とリソース最適化 |
デバッグ効率の改善 | 50%向上 | AI による根本原因分析 |
メンテナンスコスト削減 | 35%削減 | 自動的なテストケース更新と修正提案 |
成功のためのポイント
- 段階的な導入: 既存のテストを一度に置き換えるのではなく、新しいテストから MCP を活用
- チーム教育: MCP の概念と使い方をチーム全体で理解することが重要
- 継続的な改善: AI の提案を検証し、フィードバックループを構築
- セキュリティ対策: API キーの適切な管理と通信の暗号化を徹底
- パフォーマンス監視: メトリクス収集とモニタリングダッシュボードの活用
次のステップ
MCP 環境の導入が完了したら、以下のような発展的な活用も検討してみてください:
- A/B テストの自動化: AI による最適なテストパターンの提案
- クロスブラウザテストの効率化: ブラウザ固有の問題を AI が事前検出
- アクセシビリティテストの強化: AI による WCAG 準拠チェック
- パフォーマンステストの最適化: リアルタイムな負荷調整
この導入ガイドを参考に、ぜひ Playwright MCP による次世代の自動化テストを体験してください。AI との協働により、より効率的で信頼性の高いテスト環境を構築できるでしょう。
開発チームの生産性向上と、より質の高いソフトウェアの提供に向けて、MCP がもたらす革新的な変化をお楽しみください。
関連リンク
- blog
「QA は最後の砦」という幻想を捨てる。開発プロセスに QA を組み込み、手戻りをなくす方法
- blog
ドキュメントは「悪」じゃない。アジャイル開発で「ちょうどいい」ドキュメントを見つけるための思考法
- blog
「アジャイルコーチ」って何する人?チームを最強にする影の立役者の役割と、あなたがコーチになるための道筋
- blog
ペアプロって本当に効果ある?メリットだけじゃない、現場で感じたリアルな課題と乗り越え方
- blog
TDDって結局何がいいの?コードに自信が持てる、テスト駆動開発のはじめの一歩
- blog
「昨日やったこと、今日やること」の報告会じゃない!デイリースクラムをチームのエンジンにするための3つの問いかけ