| inclusion | auto |
| name | qe-n8n-workflow-testing-fundamentals |
| description | Comprehensive n8n workflow testing including execution lifecycle, node connection patterns, data flow validation, and error handling strategies. Use when testing n8n workflow automation applications. |
| tags | ["n8n","workflow","automation","testing","data-flow","nodes","triggers"] |
n8n Workflow Testing Fundamentals
<default_to_action>
When testing n8n workflows:
- VALIDATE workflow structure before execution
- TEST with realistic test data
- VERIFY node-to-node data flow
- CHECK error handling paths
- MEASURE execution performance
Quick n8n Testing Checklist:
- All nodes properly connected (no orphans)
- Trigger node correctly configured
- Data mappings between nodes valid
- Error workflows defined
- Credentials properly referenced
Critical Success Factors:
- Test each execution path separately
- Validate data transformations at each node
- Check retry and error handling behavior
- Verify integrations with external services
</default_to_action>
Quick Reference Card
When to Use
- Testing new n8n workflows
- Validating workflow changes
- Debugging failed executions
- Performance optimization
- Pre-deployment validation
n8n Workflow Components
| 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 |
Workflow Execution States
| 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 |
Workflow Structure Validation
async function validateWorkflowStructure(workflowId: string) {
const workflow = await getWorkflow(workflowId);
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');
}
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.(conn.);
}
}
}
}
orphans = workflow..( !connectedNodes.(n.));
(orphans. > ) {
.(, orphans.( n.));
}
( node workflow.) {
(node.) {
( [, ref] .(node.)) {
(! (ref.)) {
();
}
}
}
}
{ : , orphans, triggerNode };
}
Execution Testing
async function testWorkflowExecution(workflowId: string, testCases: TestCase[]) {
const results: TestResult[] = [];
for (const testCase of testCases) {
const startTime = Date.now();
const execution = await executeWorkflow(workflowId, testCase.input);
const result = await waitForCompletion(execution.id, testCase.timeout || 30000);
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;
}
testCases = [
{
: ,
: { : , : },
: { : , : },
:
},
{
: ,
: { : },
: { : },
:
},
{
: ,
: { : , : },
: { : },
:
}
];
Data Flow Validation
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
});
}
}
for (let i = 1; i < dataFlow.length; i++) {
const prev = dataFlow[i - 1];
const curr = dataFlow[i];
(prev., curr.);
}
dataFlow;
}
() {
: [] = [];
( [key, value] .(targetInput)) {
(value === && sourceOutput[key] === ) {
missingFields.(key);
}
}
(missingFields. > ) {
.(, missingFields);
}
missingFields. === ;
}
Error Handling Testing
async function testErrorHandling(workflowId: string) {
const errorScenarios = [
{
name: 'API timeout',
inject: { delay: 35000 },
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) {
const execution = await executeWithErrorInjection(workflowId, scenario.inject);
const result = await waitForCompletion(execution.id);
results.push({
scenario: scenario.,
: result. === ,
: result.?.?.?.,
: scenario.,
: (execution.),
: (execution.)
});
}
results;
}
(): <> {
errorExecutions = ({
: {
: { : executionId }
}
});
errorExecutions. > ;
}
Node Connection Patterns
Linear Flow
Trigger → Process → Transform → Output
Testing: Execute once, validate each node output
Branching Flow
Trigger → IF → [Branch A] → Merge → Output
→ [Branch B] →
Testing: Test both branches separately, verify merge behavior
Parallel Flow
Trigger → Split → [Process A] → Merge → Output
→ [Process B] →
Testing: Validate parallel execution, check merge timing
Loop Flow
Trigger → SplitInBatches → Process → [Loop back until done] → Output
Testing: Test with varying batch sizes, verify all items processed
Common Testing Patterns
Test Data Generation
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'
}
}
})
};
Execution Assertions
const workflowAssertions = {
assertCompleted: (execution) => {
expect(execution.finished).toBe(true);
expect(execution.status).toBe('success');
},
assertNodeExecuted: (execution, nodeName) => {
const nodeData = execution.data.resultData.runData[nodeName];
expect(nodeData).toBeDefined();
expect(nodeData[0].executionStatus).toBe('success');
},
assertDataTransformed: (execution, nodeName, expectedData) => {
const nodeOutput = execution.data.resultData.runData[nodeName][0].data.main[0][0].json;
expect(nodeOutput).toMatchObject(expectedData);
},
assertExecutionTime: () => {
duration = (execution.) - (execution.);
(duration).(maxMs);
}
};
Agent Coordination Hints
Memory Namespace
aqe/n8n/
├── workflows/* - Cached workflow definitions
├── test-results/* - Test execution results
├── validations/* - Validation reports
├── patterns/* - Discovered testing patterns
└── executions/* - Execution tracking
Fleet Coordination
const n8nFleet = await FleetManager.coordinate({
strategy: 'n8n-testing',
agents: [
'n8n-workflow-executor',
'n8n-node-validator',
'n8n-trigger-test',
'n8n-expression-validator',
'n8n-integration-test'
],
topology: 'parallel'
});
Related Skills
Remember
n8n workflows are JSON-based execution flows that connect 400+ services. Testing requires validating:
- Workflow structure (nodes, connections)
- Trigger reliability (webhooks, schedules)
- Data flow (transformations between nodes)
- Error handling (retry, fallback, notifications)
- Performance (execution time, resource usage)
With Agents: Use n8n-workflow-executor for execution testing, n8n-node-validator for configuration validation, and coordinate multiple agents for comprehensive workflow testing.