.claude/skills/n8n-workflow-testing-fundamentals/SKILL.md
Comprehensive n8n workflow testing including execution lifecycle, node connection patterns, data flow validation, and error handling strategies. Use when testing n8n workflow automation applications.
npx skillsauth add proffesor-for-testing/agentic-qe n8n-workflow-testing-fundamentalsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
<default_to_action> When testing n8n workflows:
Quick n8n Testing Checklist:
Critical Success Factors:
| Component | Purpose | Testing Focus | |-----------|---------|---------------| | Trigger | Starts workflow | Reliable activation, payload handling | | Action Nodes | Process data | Configuration, data mapping | | Logic Nodes | Control flow | Conditional routing, branches | | Integration Nodes | External APIs | Auth, rate limits, errors | | Error Workflow | Handle failures | Recovery, notifications |
| State | Meaning | Test Action |
|-------|---------|-------------|
| running | Currently executing | Monitor progress |
| success | Completed successfully | Validate outputs |
| failed | Execution failed | Analyze error |
| waiting | Waiting for trigger | Test trigger mechanism |
// 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
Testing: Execute once, validate each node output
Trigger → IF → [Branch A] → Merge → Output
→ [Branch B] →
Testing: Test both branches separately, verify merge behavior
Trigger → Split → [Process A] → Merge → Output
→ [Process B] →
Testing: Validate parallel execution, check merge timing
Trigger → SplitInBatches → Process → [Loop back until done] → Output
Testing: Test with varying batch sizes, verify all items processed
// 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 workflows are JSON-based execution flows that connect 400+ services. Testing requires validating:
With Agents: Use n8n-workflow-executor for execution testing, n8n-node-validator for configuration validation, and coordinate multiple agents for comprehensive workflow testing.
development
Apply XP practices including pair programming, ensemble programming, continuous integration, and sustainable pace. Use when implementing agile development practices, improving team collaboration, or adopting technical excellence practices.
development
Warehouse Management System testing patterns for inventory operations, pick/pack/ship workflows, wave management, EDI X12/EDIFACT compliance, RF/barcode scanning, and WMS-ERP integration. Use when testing WMS platforms (Blue Yonder, Manhattan, SAP EWM).
testing
Advanced visual regression testing with pixel-perfect comparison, AI-powered diff analysis, responsive design validation, and cross-browser visual consistency. Use when detecting UI regressions, validating designs, or ensuring visual consistency.
development
Comprehensive truth scoring, code quality verification, and automatic rollback system with 0.95 accuracy threshold for ensuring high-quality agent outputs and codebase reliability.