qe-n8n-workflow-testing-fundamentals について
n8n ワークフローのテスト実践ガイドです。実行前の構造確認、テストデータ、ノード間のデータフロー、エラー処理、性能測定を扱い、開発やデバッグ、デプロイ前の検証に役立ちます。
主な機能
- 01実行前のワークフロー構造確認
- 02テストデータによる実行検証
- 03ノード間のデータフロー確認
- 04エラーとリトライ経路のテスト
- 05実行性能の測定
活用シーン
- 01新しい n8n ワークフローの検証
- 02失敗したワークフロー実行のデバッグ
- 03デプロイ前の変更確認
- 04ワークフロー実行性能の最適化
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ proffesor-for-testing/ agentic-qe --skill qe-n8n-workflow-testing-fundamentalsインストール後、Claude Code で/qe-n8n-workflow-testing-fundamentalsを入力して読み込まれたか確認します。
詳細
n8n ワークフローテストの基本
<default_to_action> n8n ワークフローをテストする際は、次の手順に従います。
- 実行前にワークフローの構造を検証します
- 現実的なテストデータでテストします
- ノード間のデータフローを確認します
- エラー処理の経路を確認します
- 実行パフォーマンスを測定します
n8n のクイックテストチェックリスト:
- すべてのノードが正しく接続されている(孤立したノードがない)
- トリガーノードが正しく設定されている
- ノード間のデータマッピングが有効である
- エラーワークフローが定義されている
- 認証情報が正しく参照されている
成功のための重要な要素:
- 各実行経路を個別にテストする
- 各ノードでデータ変換を検証する
- 再試行とエラー処理の動作を確認する
- 外部サービスとの連携を確認する </default_to_action>
クイックリファレンスカード
使用するタイミング
- 新しい n8n ワークフローのテスト
- ワークフローの変更の検証
- 失敗した実行のデバッグ
- パフォーマンスの最適化
- デプロイ前の検証
n8n ワークフローのコンポーネント
| コンポーネント | 目的 | テストの重点 |
|---|---|---|
| トリガー | ワークフローを開始します | 確実な起動、ペイロードの処理 |
| アクションノード | データを処理します | 設定、データマッピング |
| ロジックノード | フローを制御します | 条件付きルーティング、分岐 |
| インテグレーションノード | 外部 API と連携します | 認証、レート制限、エラー |
| エラーワークフロー | エラーを処理します | 復旧、通知 |
ワークフローの実行状態
| 状態 | 意味 | テストアクション |
|---|---|---|
running | 実行中です | 進行状況を監視します |
success | 正常に完了しました | 出力を検証します |
failed | 実行に失敗しました | エラーを分析します |
waiting | トリガーを待機しています | トリガーの仕組みをテストします |
ワークフロー構造の検証
// Validate workflow structure before execution
async function validateWorkflowStructure(workflowId: string) {
const workflow = await getWorkflow(workflowId);
// Check for trigger node
const triggerNode = workflow.nodes.find(n =>
n.type.includes('trigger') || n.type.includes('webhook')
);
if (!triggerNode) {
throw new Error('Workflow must have a trigger node');
}
// Check for orphan nodes (no connections)
const connectedNodes = new Set();
for (const [source, targets] of Object.entries(workflow.connections)) {
connectedNodes.add(source);
for (const outputs of Object.values(targets)) {
for (const connections of outputs) {
for (const conn of connections) {
connectedNodes.add(conn.node);
}
}
}
}
const orphans = workflow.nodes.filter(n => !connectedNodes.has(n.name));
if (orphans.length > 0) {
console.warn('Orphan nodes detected:', orphans.map(n => n.name));
}
// Validate credentials
for (const node of workflow.nodes) {
if (node.credentials) {
for (const [type, ref] of Object.entries(node.credentials)) {
if (!await credentialExists(ref.id)) {
throw new Error(`Missing credential: ${type} for node ${node.name}`);
}
}
}
}
return { valid: true, orphans, triggerNode };
}
実行テスト
// Test workflow execution with various inputs
async function testWorkflowExecution(workflowId: string, testCases: TestCase[]) {
const results: TestResult[] = [];
for (const testCase of testCases) {
const startTime = Date.now();
// Execute workflow
const execution = await executeWorkflow(workflowId, testCase.input);
// Wait for completion
const result = await waitForCompletion(execution.id, testCase.timeout || 30000);
// Validate output
const outputValid = validateOutput(result.data, testCase.expected);
results.push({
testCase: testCase.name,
success: result.status === 'success' && outputValid,
duration: Date.now() - startTime,
actualOutput: result.data,
expectedOutput: testCase.expected
});
}
return results;
}
// Example test cases
const testCases = [
{
name: 'Valid customer data',
input: { name: 'John Doe', email: '[email protected]' },
expected: { processed: true, customerId: /^cust_/ },
timeout: 10000
},
{
name: 'Missing email',
input: { name: 'Jane Doe' },
expected: { error: 'Email required' },
timeout: 5000
},
{
name: 'Invalid email format',
input: { name: 'Bob', email: 'not-an-email' },
expected: { error: 'Invalid email' },
timeout: 5000
}
];
データフローの検証
// Trace data through workflow nodes
async function validateDataFlow(executionId: string) {
const execution = await getExecution(executionId);
const nodeResults = execution.data.resultData.runData;
const dataFlow: DataFlowStep[] = [];
for (const [nodeName, runs] of Object.entries(nodeResults)) {
for (const run of runs) {
dataFlow.push({
node: nodeName,
input: run.data?.main?.[0]?.[0]?.json || {},
output: run.data?.main?.[0]?.[0]?.json || {},
executionTime: run.executionTime,
status: run.executionStatus
});
}
}
// Validate data transformations
for (let i = 1; i < dataFlow.length; i++) {
const prev = dataFlow[i - 1];
const curr = dataFlow[i];
// Check if expected data passed through
validateDataMapping(prev.output, curr.input);
}
return dataFlow;
}
// Validate data mapping between nodes
function validateDataMapping(sourceOutput: any, targetInput: any) {
// Check all required fields are present
const missingFields: string[] = [];
for (const [key, value] of Object.entries(targetInput)) {
if (value === undefined && sourceOutput[key] === undefined) {
missingFields.push(key);
}
}
if (missingFields.length > 0) {
console.warn('Missing fields in data mapping:', missingFields);
}
return missingFields.length === 0;
}
エラー処理のテスト
// Test error handling paths
async function testErrorHandling(workflowId: string) {
const errorScenarios = [
{
name: 'API timeout',
inject: { delay: 35000 }, // Trigger timeout
expectedError: 'timeout'
},
{
name: 'Invalid data',
inject: { invalidField: true },
expectedError: 'validation'
},
{
name: 'Missing credentials',
inject: { removeCredentials: true },
expectedError: 'authentication'
}
];
const results: ErrorTestResult[] = [];
for (const scenario of errorScenarios) {
// Execute with error injection
const execution = await executeWithErrorInjection(workflowId, scenario.inject);
// Check error was caught
const result = await waitForCompletion(execution.id);
// Validate error handling
results.push({
scenario: scenario.name,
errorCaught: result.status === 'failed',
errorType: result.data?.resultData?.error?.type,
expectedError: scenario.expectedError,
errorWorkflowTriggered: await checkErrorWorkflowTriggered(execution.id),
alertSent: await checkAlertSent(execution.id)
});
}
return results;
}
// Verify error workflow was triggered
async function checkErrorWorkflowTriggered(executionId: string): Promise<boolean> {
const errorExecutions = await getExecutions({
filter: {
metadata: { errorTriggeredBy: executionId }
}
});
return errorExecutions.length > 0;
}
ノード接続のパターン
線形フロー
Trigger → Process → Transform → Output
テスト: 1 回実行し、各ノードの出力を検証します
分岐フロー
Trigger → IF → [Branch A] → Merge → Output
→ [Branch B] →
テスト: 両方の分岐を個別にテストし、マージの動作を確認します
並列フロー
Trigger → Split → [Process A] → Merge → Output
→ [Process B] →
テスト: 並列実行を検証し、マージのタイミングを確認します
ループフロー
Trigger → SplitInBatches → Process → [Loop back until done] → Output
テスト: バッチサイズを変えてテストし、すべての項目が処理されることを確認します
よくあるテストパターン
テストデータの生成
// Generate test data for common n8n patterns
const testDataGenerators = {
webhook: () => ({
body: { event: 'test', timestamp: new Date().toISOString() },
headers: { 'Content-Type': 'application/json' },
query: { source: 'test' }
}),
slack: () => ({
type: 'message',
channel: 'C123456',
user: 'U789012',
text: 'Test message'
}),
github: () => ({
action: 'opened',
issue: {
number: 1,
title: 'Test Issue',
body: 'Test body'
},
repository: {
full_name: 'test/repo'
}
}),
stripe: () => ({
type: 'payment_intent.succeeded',
data: {
object: {
id: 'pi_test123',
amount: 1000,
currency: 'usd'
}
}
})
};
実行時のアサーション
// Common assertions for workflow execution
const workflowAssertions = {
// Assert workflow completed
assertCompleted: (execution) => {
expect(execution.finished).toBe(true);
expect(execution.status).toBe('success');
},
// Assert specific node executed
assertNodeExecuted: (execution, nodeName) => {
const nodeData = execution.data.resultData.runData[nodeName];
expect(nodeData).toBeDefined();
expect(nodeData[0].executionStatus).toBe('success');
},
// Assert data transformation
assertDataTransformed: (execution, nodeName, expectedData) => {
const nodeOutput = execution.data.resultData.runData[nodeName][0].data.main[0][0].json;
expect(nodeOutput).toMatchObject(expectedData);
},
// Assert execution time
assertExecutionTime: (execution, maxMs) => {
const duration = new Date(execution.stoppedAt) - new Date(execution.startedAt);
expect(duration).toBeLessThan(maxMs);
}
};
エージェント連携のヒント
メモリの名前空間
aqe/n8n/
├── workflows/* - Cached workflow definitions
├── test-results/* - Test execution results
├── validations/* - Validation reports
├── patterns/* - Discovered testing patterns
└── executions/* - Execution tracking
フリート連携
// Comprehensive n8n testing with fleet
const n8nFleet = await FleetManager.coordinate({
strategy: 'n8n-testing',
agents: [
'n8n-workflow-executor', // Execute and validate
'n8n-node-validator', // Validate configurations
'n8n-trigger-test', // Test triggers
'n8n-expression-validator', // Validate expressions
'n8n-integration-test' // Test integrations
],
topology: 'parallel'
});
関連スキル
- n8n-expression-testing - 式の検証
- n8n-trigger-testing-strategies - トリガーのテスト
- n8n-integration-testing-patterns - インテグレーションのテスト
- n8n-security-testing - セキュリティの検証
お忘れなく
n8n ワークフローは、400 以上のサービスを連携する JSON ベースの実行フローです。 テストでは、次の項目を検証する必要があります。
- ワークフローの構造(ノード、接続)
- トリガーの信頼性(Webhook、スケジュール)
- データフロー(ノード間の変換)
- エラー処理(再試行、フォールバック、通知)
- パフォーマンス(実行時間、リソース使用量)
エージェントを使用する場合: 実行テストには n8n-workflow-executor を、設定の検証には n8n-node-validator を使用し、包括的なワークフローテストのために複数のエージェントを連携させます。
コメント
Markdown 対応