调试与测试
掌握N8N工作流的调试技巧和测试方法,确保自动化系统稳定运行
调试与测试
调试和测试是确保工作流稳定运行的关键技能。本章将教你如何有效调试 N8N 工作流,以及如何建立完善的测试流程。
🔍 调试基础
N8N 调试工具概览
工具 | 用途 | 位置 | 适用场景 |
---|---|---|---|
执行历史 | 查看历史执行记录 | 右侧面板 | 排查历史问题 |
节点输出 | 查看节点数据 | 节点下方 | 数据流追踪 |
错误信息 | 查看具体错误 | 节点红色标记 | 定位错误原因 |
Console日志 | 自定义调试信息 | 浏览器控制台 | 深度调试 |
执行模式
// 测试执行(Test)
// - 只执行一次
// - 显示详细数据
// - 不保存到执行历史
// 正常执行(Execute)
// - 完整执行流程
// - 保存执行历史
// - 触发所有节点
// 手动执行(Manual)
// - 跳过触发器
// - 从指定节点开始
// - 用于部分测试
🐛 常见调试场景
1. 数据流问题调试
问题:数据在节点间传递异常
调试步骤:
// 1. 在问题节点前添加 Set 节点检查数据
{
"debug_node_name": "before_problem_node",
"debug_data_type": "{{ typeof $json }}",
"debug_data_keys": "{{ Object.keys($json) }}",
"debug_item_count": "{{ $input.all().length }}",
"debug_sample_data": "{{ JSON.stringify($json, null, 2) }}",
"original_data": "{{ $json }}"
}
// 2. 使用 Function 节点详细分析
const items = $input.all();
console.log('Total items:', items.length);
items.forEach((item, index) => {
console.log(`Item ${index}:`, {
type: typeof item.json,
keys: Object.keys(item.json),
values: item.json
});
});
// 检查数据完整性
const hasRequiredFields = items.every(item =>
item.json.id && item.json.email
);
console.log('All items have required fields:', hasRequiredFields);
return items;
2. API 调用问题调试
问题:HTTP Request 节点调用失败
调试方法:
// 1. 在 HTTP Request 节点中启用详细日志
// Settings →
Continue On Fail: true // 继续执行不中断
Response Format: "Auto-detect"
// 2. 添加错误处理 Function 节点
const response = $input.first().json;
if (response.error) {
console.error('API Error:', {
status: response.error.httpCode,
message: response.error.message,
details: response.error.description
});
// 记录请求详情
console.log('Request details:', {
url: $node["HTTP Request"].json.url,
method: $node["HTTP Request"].json.method,
headers: $node["HTTP Request"].json.headers
});
}
// 3. 测试API连接性
// 使用简单的GET请求测试基础连接
GET https://httpbin.org/status/200
3. 表达式语法调试
问题:表达式计算结果不符合预期
调试技巧:
// 1. 分步调试复杂表达式
// 错误的复杂表达式
{{ $json.orders.filter(o => o.status === 'paid').reduce((sum, o) => sum + o.amount, 0) }}
// 分步调试
// Step 1: 检查原始数据
"debug_orders_raw": "{{ $json.orders }}"
// Step 2: 检查过滤结果
"debug_paid_orders": "{{ $json.orders.filter(o => o.status === 'paid') }}"
// Step 3: 检查最终计算
"debug_total_amount": "{{ $json.orders.filter(o => o.status === 'paid').reduce((sum, o) => sum + o.amount, 0) }}"
// 2. 使用 Function 节点验证逻辑
const orders = $json.orders;
console.log('Original orders:', orders);
const paidOrders = orders.filter(o => o.status === 'paid');
console.log('Paid orders:', paidOrders);
const totalAmount = paidOrders.reduce((sum, o) => sum + o.amount, 0);
console.log('Total amount:', totalAmount);
return [{ json: { totalAmount } }];
4. 条件逻辑调试
问题:IF 节点条件判断错误
调试方法:
// 1. 详细记录条件判断过程
// 在 Function 节点中验证条件
const data = $json;
const conditions = {
ageCheck: data.age >= 18,
emailCheck: data.email && data.email.includes('@'),
statusCheck: data.status === 'active',
scoreCheck: data.score > 80
};
console.log('Condition checks:', conditions);
console.log('Final result:', Object.values(conditions).every(Boolean));
// 2. 类型检查
console.log('Data types:', {
age: typeof data.age,
email: typeof data.email,
status: typeof data.status,
score: typeof data.score
});
// 3. 边界值测试
const testCases = [
{ age: 17, expected: false },
{ age: 18, expected: true },
{ age: null, expected: false }
];
testCases.forEach(test => {
const result = test.age >= 18;
console.log(`Age ${test.age}: ${result} (expected: ${test.expected})`);
});
🧪 测试策略
1. 单元测试(节点级别)
为每个关键节点创建测试:
// Function 节点:数据验证测试
function testDataValidation() {
const testData = [
{ email: '[email protected]', expected: true },
{ email: 'invalid-email', expected: false },
{ email: '', expected: false },
{ email: null, expected: false }
];
testData.forEach(test => {
const isValid = validateEmail(test.email);
console.assert(
isValid === test.expected,
`Email validation failed for ${test.email}`
);
});
console.log('Email validation tests passed!');
}
function validateEmail(email) {
if (!email) return false;
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
testDataValidation();
2. 集成测试(工作流级别)
测试完整的工作流程:
// 创建测试工作流
// 1. 准备测试数据
const testWebhookData = {
orderId: 'TEST-001',
customerEmail: '[email protected]',
amount: 299.99,
items: [
{ productId: 'PROD-001', quantity: 2 }
]
};
// 2. 发送测试请求
POST https://your-n8n.com/webhook/test-order
Body: testWebhookData
// 3. 验证输出结果
// 在最后一个节点添加验证
const expectedResults = {
emailSent: true,
orderCreated: true,
inventoryUpdated: true
};
const actualResults = {
emailSent: $('Send Email').json.messageId ? true : false,
orderCreated: $('Create Order').json.orderId ? true : false,
inventoryUpdated: $('Update Inventory').json.updated === 'success'
};
// 对比结果
const testPassed = Object.keys(expectedResults).every(key =>
expectedResults[key] === actualResults[key]
);
if (!testPassed) {
console.error('Integration test failed:', {
expected: expectedResults,
actual: actualResults
});
throw new Error('Integration test failed');
}
console.log('Integration test passed!');
3. 压力测试
测试工作流在高负载下的表现:
// 并发测试脚本
const testConcurrency = async () => {
const concurrentRequests = 10;
const promises = [];
for (let i = 0; i < concurrentRequests; i++) {
const testData = {
id: `test-${i}`,
timestamp: new Date().toISOString()
};
promises.push(
fetch('https://your-n8n.com/webhook/test', {
method: 'POST',
body: JSON.stringify(testData),
headers: { 'Content-Type': 'application/json' }
})
);
}
const results = await Promise.allSettled(promises);
const successes = results.filter(r => r.status === 'fulfilled').length;
console.log(`Concurrent test results: ${successes}/${concurrentRequests} succeeded`);
};
📊 监控和日志
1. 自定义日志系统
// Function 节点:结构化日志
class WorkflowLogger {
constructor(workflowId, nodeId) {
this.workflowId = workflowId;
this.nodeId = nodeId;
this.timestamp = new Date().toISOString();
}
info(message, data = {}) {
console.log(JSON.stringify({
level: 'INFO',
workflowId: this.workflowId,
nodeId: this.nodeId,
timestamp: this.timestamp,
message,
data
}));
}
error(message, error = {}) {
console.error(JSON.stringify({
level: 'ERROR',
workflowId: this.workflowId,
nodeId: this.nodeId,
timestamp: this.timestamp,
message,
error: {
name: error.name,
message: error.message,
stack: error.stack
}
}));
}
}
// 使用示例
const logger = new WorkflowLogger('order-processing', 'validate-order');
logger.info('Starting order validation', { orderId: $json.orderId });
try {
// 业务逻辑
const result = validateOrder($json);
logger.info('Order validation completed', { result });
} catch (error) {
logger.error('Order validation failed', error);
throw error;
}
2. 性能监控
// Function 节点:性能监控
const performanceMonitor = {
startTime: Date.now(),
start(operation) {
this[`${operation}_start`] = Date.now();
},
end(operation) {
const duration = Date.now() - this[`${operation}_start`];
console.log(`Performance: ${operation} took ${duration}ms`);
return duration;
},
getTotalDuration() {
return Date.now() - this.startTime;
}
};
// 监控API调用
performanceMonitor.start('api_call');
const apiResponse = await callExternalAPI($json);
performanceMonitor.end('api_call');
// 监控数据处理
performanceMonitor.start('data_processing');
const processedData = processLargeDataset($json);
performanceMonitor.end('data_processing');
console.log(`Total workflow duration: ${performanceMonitor.getTotalDuration()}ms`);
3. 错误追踪
// Function 节点:错误上下文收集
const collectErrorContext = (error, inputData) => {
const context = {
error: {
name: error.name,
message: error.message,
stack: error.stack
},
environment: {
nodeEnv: $env.NODE_ENV,
timestamp: new Date().toISOString(),
workflowId: $workflow.id,
executionId: $execution.id
},
inputData: {
itemCount: $input.all().length,
sampleData: JSON.stringify(inputData).substring(0, 500)
},
systemInfo: {
memoryUsage: process.memoryUsage ? process.memoryUsage() : 'N/A',
uptime: process.uptime ? process.uptime() : 'N/A'
}
};
return context;
};
// 使用示例
try {
// 业务逻辑
const result = processData($json);
} catch (error) {
const errorContext = collectErrorContext(error, $json);
// 发送错误报告
await sendErrorReport(errorContext);
// 重新抛出错误
throw error;
}
🔧 调试工具和技巧
1. 数据检查器
// Function 节点:通用数据检查器
const dataInspector = (data, label = 'Data') => {
console.log(`\n=== ${label} Inspector ===`);
console.log('Type:', typeof data);
console.log('Constructor:', data?.constructor?.name);
if (Array.isArray(data)) {
console.log('Array length:', data.length);
console.log('First item:', data[0]);
console.log('Sample items:', data.slice(0, 3));
} else if (typeof data === 'object' && data !== null) {
console.log('Object keys:', Object.keys(data));
console.log('Object values preview:', JSON.stringify(data, null, 2).substring(0, 200));
} else {
console.log('Value:', data);
}
console.log('=== End Inspector ===\n');
return data;
};
// 在管道中使用
const inputData = dataInspector($json, 'Input Data');
const processedData = processData(inputData);
const finalData = dataInspector(processedData, 'Processed Data');
2. 断点调试
// Function 节点:条件断点
const debugBreakpoint = (condition, data, label) => {
if (condition) {
console.log(`\n🔴 BREAKPOINT: ${label}`);
console.log('Condition:', condition);
console.log('Data at breakpoint:', JSON.stringify(data, null, 2));
console.log('Call stack available in browser DevTools');
// 在浏览器中触发断点
debugger;
}
return data;
};
// 使用示例
const userData = $json;
debugBreakpoint(
userData.age < 0, // 异常条件
userData,
'Invalid age detected'
);
3. A/B 测试框架
// Function 节点:A/B测试
const abTestConfig = {
testName: 'email_template_test',
variants: {
A: { template: 'template_a.html', weight: 50 },
B: { template: 'template_b.html', weight: 50 }
}
};
const selectVariant = (userId, config) => {
const hash = hashUserId(userId);
const totalWeight = Object.values(config.variants)
.reduce((sum, v) => sum + v.weight, 0);
let currentWeight = 0;
const targetWeight = (hash % totalWeight);
for (const [variant, data] of Object.entries(config.variants)) {
currentWeight += data.weight;
if (targetWeight < currentWeight) {
return { variant, ...data };
}
}
};
const variant = selectVariant($json.userId, abTestConfig);
console.log(`User ${$json.userId} assigned to variant ${variant.variant}`);
// 记录测试参与
await logABTestParticipation($json.userId, abTestConfig.testName, variant.variant);
📋 测试清单
开发阶段测试
- 每个节点单独测试
- 数据类型验证
- 边界条件测试
- 错误处理验证
- 表达式语法检查
集成测试
- 端到端流程测试
- 多分支路径测试
- 异常数据处理
- 外部依赖模拟
- 性能基准测试
生产前测试
- 生产数据测试
- 负载测试
- 故障恢复测试
- 监控告警测试
- 备份恢复测试
🎯 最佳实践
1. 测试数据管理
// 创建可重用的测试数据集
const testDataSets = {
validUser: {
id: 'test-001',
email: '[email protected]',
age: 25,
status: 'active'
},
invalidUser: {
id: '',
email: 'invalid-email',
age: -1,
status: 'unknown'
},
edgeCaseUser: {
id: 'test-999',
email: '[email protected]',
age: 0,
status: null
}
};
2. 环境隔离
// 使用环境变量控制测试行为
const isTestEnvironment = $env.NODE_ENV === 'test';
const isDevelopment = $env.NODE_ENV === 'development';
if (isTestEnvironment) {
// 使用模拟API
apiUrl = 'http://localhost:3000/mock-api';
} else {
// 使用真实API
apiUrl = $env.PRODUCTION_API_URL;
}
3. 自动化测试
// 在工作流中集成自动化测试
const runAutomatedTests = async () => {
const testSuite = [
() => testEmailValidation(),
() => testDataTransformation(),
() => testAPIIntegration()
];
const results = [];
for (const test of testSuite) {
try {
await test();
results.push({ status: 'passed', test: test.name });
} catch (error) {
results.push({ status: 'failed', test: test.name, error: error.message });
}
}
// 发送测试报告
await sendTestReport(results);
return results;
};
🚀 下一步学习
掌握调试和测试后,继续深入学习:
良好的调试和测试习惯是构建可靠自动化系统的基石。通过系统性的测试和细致的调试,你能确保工作流在各种情况下都能稳定运行!