Cursor/Claude CodeでRPAのテストコードを自動生成する実践例(Playwright)
#Cursor#Claude Code#Playwright#テスト自動化#RPA#コーディングエージェント
はじめに
RPAを開発した後、その動作を検証するテストコードを書くのは時間がかかります。特に、正常系・異常系のテストケース、エッジケースのテスト、モックを使ったテストなど、様々なパターンを実装する必要があります。
CursorやClaude Codeなどのコーディングエージェントを活用することで、RPAのテストコードを効率的に自動生成できます。この記事では、実際のプロジェクトで活用した経験を基に、テストコードの自動生成方法を解説します。
RPAテストコードの重要性
1. 動作検証の自動化
RPAの動作を手動で確認するのは時間がかかり、見落としも発生しがちです。テストコードを書くことで、自動的に動作を検証できます。
2. リグレッション防止
RPAのコードを変更した際、既存の機能が壊れていないかを確認するためにテストコードが重要です。
3. ドキュメントとしての役割
テストコードは、RPAの期待される動作を明確に示すドキュメントとしても機能します。
実践例1:基本的なテストコードの生成
プロンプト設計
効果的なプロンプトの例:
PlaywrightでRPAのテストコードを作成してください。
対象RPA:
- ログイン処理: login(page, email, password)
- CSVダウンロード処理: downloadSalesReport(page)
テスト要件:
- 正常系: ログイン成功、CSVダウンロード成功
- 異常系: 不正な認証情報、要素が見つからない
- エッジケース: タイムアウト、ネットワークエラー
使用ライブラリ: @playwright/test
テストフレームワーク: Jest風の記述
生成されたテストコード例
import { test, expect } from '@playwright/test';
import { login, downloadSalesReport } from '../src/rpa';
test.describe('RPA Tests', () => {
test.beforeEach(async ({ page }) => {
// 各テスト前にベースURLに移動
await page.goto('https://example.com/admin');
});
test.describe('ログイン処理', () => {
test('正常系: 正しい認証情報でログイン成功', async ({ page }) => {
await login(page, '[email protected]', 'correctpassword');
// ダッシュボードが表示されることを確認
await expect(page.getByTestId('dashboard')).toBeVisible();
await expect(page).toHaveURL(/.*dashboard/);
});
test('異常系: 不正なメールアドレスでログイン失敗', async ({ page }) => {
await expect(async () => {
await login(page, '[email protected]', 'password123');
}).rejects.toThrow();
// エラーメッセージが表示されることを確認
await expect(page.getByText('ログインに失敗しました')).toBeVisible();
});
test('異常系: 不正なパスワードでログイン失敗', async ({ page }) => {
await expect(async () => {
await login(page, '[email protected]', 'wrongpassword');
}).rejects.toThrow();
await expect(page.getByText('パスワードが正しくありません')).toBeVisible();
});
test('異常系: 空の認証情報でログイン失敗', async ({ page }) => {
await expect(async () => {
await login(page, '', '');
}).rejects.toThrow();
});
});
test.describe('CSVダウンロード処理', () => {
test('正常系: CSVダウンロード成功', async ({ page, context }) => {
// ログイン
await login(page, '[email protected]', 'correctpassword');
// ダウンロードを監視
const [download] = await Promise.all([
page.waitForEvent('download'),
downloadSalesReport(page),
]);
// ファイルがダウンロードされたことを確認
expect(download.suggestedFilename()).toContain('.csv');
const path = await download.path();
expect(path).toBeTruthy();
// ファイルの内容を確認(オプション)
const fs = require('fs');
const content = fs.readFileSync(path, 'utf-8');
expect(content).toContain('売上');
});
test('異常系: ログインせずにダウンロード試行', async ({ page }) => {
await expect(async () => {
await downloadSalesReport(page);
}).rejects.toThrow();
});
test('異常系: 要素が見つからない', async ({ page }) => {
await login(page, '[email protected]', 'correctpassword');
// 存在しないボタンをクリックしようとする
await expect(async () => {
await page.getByRole('button', { name: '存在しないボタン' }).click({ timeout: 1000 });
}).rejects.toThrow();
});
});
});
実践例2:統合テストの生成
複数の処理を組み合わせた統合テストを生成する例です。
エージェントへの指示
このRPAの統合テストを作成してください。
RPAフロー:
1. ログイン
2. ダッシュボード確認
3. 売上レポートページに移動
4. CSVダウンロード
5. ダウンロードしたCSVの内容を検証
テスト要件:
- エンドツーエンドの動作確認
- 各ステップで適切な待機処理
- エラー時のスクリーンショット保存
- テストデータのクリーンアップ
生成されたコード例
import { test, expect } from '@playwright/test';
import * as fs from 'fs';
import * as path from 'path';
test.describe('RPA統合テスト', () => {
const downloadDir = path.join(__dirname, '../downloads');
test.beforeAll(() => {
// ダウンロードディレクトリを作成
if (!fs.existsSync(downloadDir)) {
fs.mkdirSync(downloadDir, { recursive: true });
}
});
test.afterEach(async ({ page }) => {
// テスト失敗時にスクリーンショットを保存
if (test.info().status === 'failed') {
const screenshotPath = path.join(
downloadDir,
`failed-${test.info().title.replace(/\s/g, '-')}-${Date.now()}.png`
);
await page.screenshot({ path: screenshotPath, fullPage: true });
console.log(`スクリーンショット保存: ${screenshotPath}`);
}
});
test('エンドツーエンド: ログインからCSVダウンロードまで', async ({ page, context }) => {
// 1. ログイン
await login(page, process.env.TEST_EMAIL!, process.env.TEST_PASSWORD!);
// 2. ダッシュボードが表示されることを確認
await expect(page.getByTestId('dashboard')).toBeVisible({ timeout: 10000 });
await expect(page).toHaveURL(/.*dashboard/);
// 3. 売上レポートページに移動
await page.getByRole('link', { name: '売上レポート' }).click();
await expect(page).toHaveURL(/.*sales-report/);
await expect(page.getByRole('heading', { name: '売上レポート' })).toBeVisible();
// 4. CSVダウンロード
const [download] = await Promise.all([
page.waitForEvent('download'),
page.getByRole('button', { name: 'CSVダウンロード' }).click(),
]);
// 5. ダウンロードしたCSVの内容を検証
const downloadPath = await download.path();
expect(downloadPath).toBeTruthy();
const csvContent = fs.readFileSync(downloadPath!, 'utf-8');
expect(csvContent).toContain('売上');
expect(csvContent.split('\n').length).toBeGreaterThan(1); // ヘッダー + データ行
// CSVの構造を検証
const lines = csvContent.split('\n');
const header = lines[0].split(',');
expect(header).toContain('日付');
expect(header).toContain('売上金額');
});
});
実践例3:モックを使ったテスト
外部APIやデータベースに依存しないテストを生成する例です。
エージェントへの指示
このRPAのモックを使ったテストを作成してください。
要件:
- 外部APIの呼び出しをモック
- データベースアクセスをモック
- ファイルシステム操作をモック
- 各モックで正常系・異常系をテスト
使用ライブラリ: @playwright/test, MSW (Mock Service Worker)
生成されたコード例
import { test, expect } from '@playwright/test';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
// モックサーバーの設定
const server = setupServer(
// 正常系: API成功
rest.get('https://api.example.com/sales', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
sales: [
{ date: '2026-01-01', amount: 100000 },
{ date: '2026-01-02', amount: 150000 },
],
})
);
}),
// 異常系: APIエラー
rest.get('https://api.example.com/sales-error', (req, res, ctx) => {
return res(ctx.status(500), ctx.json({ error: 'Internal Server Error' }));
})
);
test.beforeAll(() => server.listen());
test.afterEach(() => server.resetHandlers());
test.afterAll(() => server.close());
test.describe('モックを使ったRPAテスト', () => {
test('正常系: API成功時の処理', async ({ page }) => {
await page.goto('https://example.com/admin');
await login(page, '[email protected]', 'password');
// APIが正常に呼び出されることを確認
const response = await page.waitForResponse('https://api.example.com/sales');
expect(response.status()).toBe(200);
const data = await response.json();
expect(data.sales).toHaveLength(2);
});
test('異常系: APIエラー時の処理', async ({ page }) => {
await page.goto('https://example.com/admin');
await login(page, '[email protected]', 'password');
// エラーページに移動
await page.goto('https://example.com/admin/sales-error');
// エラーメッセージが表示されることを確認
await expect(page.getByText('データの取得に失敗しました')).toBeVisible();
});
});
実践例4:パフォーマンステストの生成
RPAの実行時間やリソース使用量を測定するテストを生成する例です。
エージェントへの指示
このRPAのパフォーマンステストを作成してください。
要件:
- 実行時間の測定
- メモリ使用量の監視
- ネットワークリクエスト数のカウント
- パフォーマンス基準値との比較
- スローダウンの検出
生成されたコード例
import { test, expect } from '@playwright/test';
test.describe('RPAパフォーマンステスト', () => {
test('ログイン処理の実行時間', async ({ page }) => {
const startTime = Date.now();
await page.goto('https://example.com/admin');
await login(page, '[email protected]', 'password');
const endTime = Date.now();
const executionTime = endTime - startTime;
// 実行時間が5秒以内であることを確認
expect(executionTime).toBeLessThan(5000);
console.log(`ログイン処理時間: ${executionTime}ms`);
});
test('CSVダウンロード処理の実行時間', async ({ page }) => {
await login(page, '[email protected]', 'password');
const startTime = Date.now();
await downloadSalesReport(page);
const endTime = Date.now();
const executionTime = endTime - startTime;
expect(executionTime).toBeLessThan(10000); // 10秒以内
console.log(`CSVダウンロード処理時間: ${executionTime}ms`);
});
test('ネットワークリクエスト数の監視', async ({ page }) => {
const requests: string[] = [];
page.on('request', (request) => {
requests.push(request.url());
});
await page.goto('https://example.com/admin');
await login(page, '[email protected]', 'password');
await downloadSalesReport(page);
// リクエスト数が適切な範囲内であることを確認
expect(requests.length).toBeLessThan(50);
console.log(`総リクエスト数: ${requests.length}`);
});
test('メモリ使用量の監視', async ({ page, context }) => {
const client = await context.newCDPSession(page);
await login(page, '[email protected]', 'password');
// メモリ使用量を取得
const memoryInfo = await client.send('Runtime.getHeapUsage');
const usedMemoryMB = memoryInfo.usedSize / 1024 / 1024;
// メモリ使用量が100MB以内であることを確認
expect(usedMemoryMB).toBeLessThan(100);
console.log(`メモリ使用量: ${usedMemoryMB.toFixed(2)}MB`);
});
});
コーディングエージェント活用のベストプラクティス
1. テストケースの網羅性
エージェントに以下の観点でテストケースを生成してもらいます。
- 正常系: 期待通りの動作
- 異常系: エラーケース
- エッジケース: 境界値や特殊な状況
- 統合テスト: 複数処理の組み合わせ
2. テストデータの管理
テストデータを適切に管理するコードも生成してもらいます。
// テストデータの管理
const testData = {
validCredentials: {
email: '[email protected]',
password: 'password123',
},
invalidCredentials: {
email: '[email protected]',
password: 'wrongpassword',
},
};
3. テストの保守性
テストコードが保守しやすいように、以下の点を意識します。
- DRY原則: 重複を避ける
- 可読性: テストの意図が明確
- 独立性: テスト同士が依存しない
注意点と制限事項
テストコードの限界
- UI変更への対応: UIが変わるとテストが失敗する可能性がある
- 実行環境: テスト環境と本番環境の違いに注意
- フレーキーテスト: タイミングによって失敗する可能性
エージェントの限界
- 最新のAPI: Playwrightの最新機能を理解していない場合がある
- テスト設計: テストの設計思想は人間が判断する必要がある
まとめ
CursorやClaude Codeなどのコーディングエージェントを活用することで、RPAのテストコードを効率的に自動生成できます。
効果的な活用方法:
- 具体的で構造化されたプロンプトを使用
- 正常系・異常系・エッジケースを網羅
- モックを使った独立したテスト
- パフォーマンステストも自動生成
改善前:
- テストコードの作成に数時間かかる
- テストケースの網羅性が不十分
- テストの保守性が低い
改善後:
- エージェントの支援でテストコードを迅速に生成
- 網羅的なテストケースが自動生成
- 保守性の高いテストコードが実現
コーディングエージェントは、RPAテスト開発の「強力なパートナー」として活用することで、より効率的で信頼性の高いテストを実現できます。
関連リンク: