Skip to content

n8n 测试与调试技巧 - 确保工作流质量

工作流的质量直接影响业务的稳定性。一个经过充分测试的工作流能够在各种情况下稳定运行,而调试技巧则能帮助我们快速定位和解决问题。

测试策略

测试金字塔

在 n8n 工作流测试中,我们可以采用测试金字塔的思想:

mermaid
graph TD
    A[端到端测试] --> B[集成测试]
    B --> C[单元测试]

    style A fill:#ff9999
    style B fill:#ffcc99
    style C fill:#99ff99

单元测试(基础层)

  • 测试单个节点的功能
  • 验证数据转换逻辑
  • 检查表达式计算结果

集成测试(中间层)

  • 测试节点间的数据流
  • 验证外部 API 集成
  • 检查数据库操作

端到端测试(顶层)

  • 测试完整的业务流程
  • 验证用户场景
  • 检查系统整体表现

测试环境准备

测试数据管理

javascript
// 测试数据工厂
class TestDataFactory {
  constructor() {
    this.templates = {
      user: {
        id: () => Math.floor(Math.random() * 10000),
        name: () => `测试用户${Math.floor(Math.random() * 100)}`,
        email: () => `test${Math.floor(Math.random() * 1000)}@example.com`,
        phone: () => `138${Math.floor(Math.random() * 100000000).toString().padStart(8, '0')}`,
        createdAt: () => new Date().toISOString()
      },
      order: {
        id: () => `ORD${Date.now()}`,
        userId: () => Math.floor(Math.random() * 10000),
        amount: () => Math.floor(Math.random() * 10000) / 100,
        status: () => ['pending', 'paid', 'shipped', 'completed'][Math.floor(Math.random() * 4)],
        items: () => this.generateOrderItems()
      }
    };
  }

  // 生成测试数据
  generate(type, count = 1, overrides = {}) {
    const template = this.templates[type];
    if (!template) {
      throw new Error(`未知的数据类型: ${type}`);
    }

    const results = [];
    for (let i = 0; i < count; i++) {
      const data = {};
      Object.entries(template).forEach(([key, generator]) => {
        data[key] = typeof generator === 'function' ? generator() : generator;
      });

      // 应用覆盖值
      Object.assign(data, overrides);
      results.push(data);
    }

    return count === 1 ? results[0] : results;
  }

  // 生成订单项目
  generateOrderItems() {
    const itemCount = Math.floor(Math.random() * 5) + 1;
    const items = [];

    for (let i = 0; i < itemCount; i++) {
      items.push({
        productId: Math.floor(Math.random() * 1000),
        quantity: Math.floor(Math.random() * 10) + 1,
        price: Math.floor(Math.random() * 10000) / 100
      });
    }

    return items;
  }
}

// 使用示例
const testData = new TestDataFactory();

// 生成单个用户
const user = testData.generate('user', 1, { name: '张三' });

// 生成多个订单
const orders = testData.generate('order', 5);

单元测试

单元测试专注于测试工作流中的单个节点或小的功能单元。

节点功能测试

javascript
// 节点测试框架
class NodeTester {
  constructor(nodeType) {
    this.nodeType = nodeType;
    this.testCases = [];
  }

  // 添加测试用例
  addTestCase(name, input, expectedOutput, parameters = {}) {
    this.testCases.push({
      name,
      input,
      expectedOutput,
      parameters
    });
  }

  // 执行测试
  async runTests() {
    const results = [];

    for (const testCase of this.testCases) {
      try {
        const result = await this.executeNode(
          testCase.input,
          testCase.parameters
        );

        const passed = this.compareResults(result, testCase.expectedOutput);

        results.push({
          name: testCase.name,
          passed,
          input: testCase.input,
          expected: testCase.expectedOutput,
          actual: result,
          error: null
        });
      } catch (error) {
        results.push({
          name: testCase.name,
          passed: false,
          error: error.message
        });
      }
    }

    return results;
  }

  // 比较结果
  compareResults(actual, expected) {
    return JSON.stringify(actual) === JSON.stringify(expected);
  }
}

// 使用示例:测试数据转换节点
const dataTransformTester = new NodeTester('Set');

dataTransformTester.addTestCase(
  '用户名格式化',
  [{ json: { firstName: 'John', lastName: 'Doe' } }],
  [{ json: { fullName: 'John Doe' } }],
  {
    values: {
      fullName: '={{ $json.firstName }} {{ $json.lastName }}'
    }
  }
);

dataTransformTester.addTestCase(
  '邮箱验证',
  [{ json: { email: 'test@example.com' } }],
  [{ json: { isValidEmail: true } }],
  {
    values: {
      isValidEmail: '={{ $json.email.includes("@") }}'
    }
  }
);

集成测试

集成测试验证多个节点之间的协作和数据流。

工作流集成测试

javascript
// 工作流集成测试框架
class WorkflowIntegrationTester {
  constructor(workflowDefinition) {
    this.workflow = workflowDefinition;
    this.mockServices = new Map();
  }

  // 添加服务模拟
  mockService(serviceName, mockImplementation) {
    this.mockServices.set(serviceName, mockImplementation);
  }

  // 执行集成测试
  async testWorkflow(triggerData, expectedResults) {
    // 设置模拟服务
    this.setupMocks();

    try {
      // 执行工作流
      const results = await this.executeWorkflow(triggerData);

      // 验证结果
      const validationResults = this.validateResults(results, expectedResults);

      return {
        success: validationResults.allPassed,
        results: validationResults.details,
        executionData: results
      };
    } finally {
      // 清理模拟服务
      this.cleanupMocks();
    }
  }

  // 验证结果
  validateResults(actual, expected) {
    const details = [];
    let allPassed = true;

    expected.forEach((expectation, index) => {
      const actualResult = actual[index];
      const passed = this.deepEqual(actualResult, expectation);

      if (!passed) {
        allPassed = false;
      }

      details.push({
        index,
        passed,
        expected: expectation,
        actual: actualResult
      });
    });

    return { allPassed, details };
  }
}

// 使用示例:测试客户数据同步工作流
const customerSyncTester = new WorkflowIntegrationTester(customerSyncWorkflow);

// 模拟 CRM API
customerSyncTester.mockService('crmApi', {
  getCustomers: async () => [
    { id: 1, name: '张三', email: 'zhang@example.com' },
    { id: 2, name: '李四', email: 'li@example.com' }
  ]
});

// 模拟数据库
customerSyncTester.mockService('database', {
  insertCustomers: async (customers) => {
    return { inserted: customers.length };
  }
});

// 执行测试
const testResult = await customerSyncTester.testWorkflow(
  { trigger: 'manual' },
  [
    { success: true, customersProcessed: 2 }
  ]
);

端到端测试

端到端测试验证完整的业务流程,从触发到最终结果。

业务流程测试

javascript
// 端到端测试框架
class E2ETestRunner {
  constructor() {
    this.testSuites = [];
    this.environment = process.env.TEST_ENV || 'staging';
  }

  // 添加测试套件
  addTestSuite(name, tests) {
    this.testSuites.push({ name, tests });
  }

  // 运行所有测试
  async runAllTests() {
    const results = {
      totalSuites: this.testSuites.length,
      passedSuites: 0,
      failedSuites: 0,
      details: []
    };

    for (const suite of this.testSuites) {
      const suiteResult = await this.runTestSuite(suite);
      results.details.push(suiteResult);

      if (suiteResult.allPassed) {
        results.passedSuites++;
      } else {
        results.failedSuites++;
      }
    }

    return results;
  }

  // 运行测试套件
  async runTestSuite(suite) {
    const suiteResult = {
      name: suite.name,
      tests: [],
      allPassed: true,
      duration: 0
    };

    const startTime = Date.now();

    for (const test of suite.tests) {
      const testResult = await this.runSingleTest(test);
      suiteResult.tests.push(testResult);

      if (!testResult.passed) {
        suiteResult.allPassed = false;
      }
    }

    suiteResult.duration = Date.now() - startTime;
    return suiteResult;
  }

  // 运行单个测试
  async runSingleTest(test) {
    const startTime = Date.now();

    try {
      // 设置测试环境
      await this.setupTestEnvironment(test.setup);

      // 执行测试步骤
      const result = await this.executeTestSteps(test.steps);

      // 验证结果
      const validation = await this.validateTestResult(result, test.expected);

      return {
        name: test.name,
        passed: validation.passed,
        duration: Date.now() - startTime,
        result: result,
        validation: validation,
        error: null
      };
    } catch (error) {
      return {
        name: test.name,
        passed: false,
        duration: Date.now() - startTime,
        error: error.message
      };
    } finally {
      // 清理测试环境
      await this.cleanupTestEnvironment(test.cleanup);
    }
  }
}

// 使用示例:订单处理端到端测试
const e2eRunner = new E2ETestRunner();

e2eRunner.addTestSuite('订单处理流程', [
  {
    name: '完整订单处理流程',
    setup: {
      // 准备测试数据
      createTestUser: true,
      createTestProducts: true
    },
    steps: [
      { action: 'createOrder', data: { userId: 'test-user', items: [{ productId: 1, quantity: 2 }] } },
      { action: 'processPayment', data: { orderId: '{{orderId}}', amount: 100 } },
      { action: 'fulfillOrder', data: { orderId: '{{orderId}}' } },
      { action: 'sendNotification', data: { orderId: '{{orderId}}', type: 'completion' } }
    ],
    expected: {
      orderStatus: 'completed',
      paymentStatus: 'paid',
      notificationSent: true
    },
    cleanup: {
      deleteTestData: true
    }
  }
]);

// 运行测试
const testResults = await e2eRunner.runAllTests();
console.log('测试结果:', testResults);

调试技巧

当工作流出现问题时,有效的调试技巧能帮助我们快速定位和解决问题。

调试工具使用

内置调试功能

n8n 提供了多种内置的调试工具:

  1. 执行历史查看:查看每次执行的详细信息
  2. 节点数据检查:查看每个节点的输入输出数据
  3. 错误信息分析:详细的错误堆栈和上下文信息
  4. 执行时间分析:识别性能瓶颈

自定义调试节点

javascript
// 调试辅助节点
class DebugHelper {
  // 数据检查点
  static checkpoint(data, label = 'Debug') {
    console.log(`[${label}] 检查点数据:`, JSON.stringify(data, null, 2));

    // 数据验证
    const validation = this.validateData(data);
    if (!validation.valid) {
      console.warn(`[${label}] 数据验证失败:`, validation.errors);
    }

    return data; // 透传数据
  }

  // 性能监控
  static performanceMonitor(fn, label = 'Operation') {
    return async (...args) => {
      const startTime = Date.now();
      const startMemory = process.memoryUsage().heapUsed;

      try {
        const result = await fn(...args);
        const duration = Date.now() - startTime;
        const memoryUsed = process.memoryUsage().heapUsed - startMemory;

        console.log(`[${label}] 性能统计:`, {
          duration: `${duration}ms`,
          memory: `${Math.round(memoryUsed / 1024)}KB`
        });

        return result;
      } catch (error) {
        const duration = Date.now() - startTime;
        console.error(`[${label}] 执行失败 (${duration}ms):`, error.message);
        throw error;
      }
    };
  }

  // 数据验证
  static validateData(data) {
    const errors = [];

    if (!data) {
      errors.push('数据为空');
    } else if (Array.isArray(data)) {
      if (data.length === 0) {
        errors.push('数组为空');
      }
      data.forEach((item, index) => {
        if (!item.json) {
          errors.push(`项目 ${index} 缺少 json 属性`);
        }
      });
    } else if (typeof data === 'object') {
      if (!data.json) {
        errors.push('对象缺少 json 属性');
      }
    }

    return {
      valid: errors.length === 0,
      errors
    };
  }
}

// 在工作流中使用
const debuggedData = DebugHelper.checkpoint(items, '用户数据处理前');

const processUsers = DebugHelper.performanceMonitor(async (users) => {
  // 处理用户数据的逻辑
  return users.map(user => ({
    ...user,
    processed: true
  }));
}, '用户数据处理');

const result = await processUsers(debuggedData);

常见问题排查

问题分类和解决方案

javascript
// 问题诊断工具
class WorkflowDiagnostic {
  constructor() {
    this.commonIssues = [
      {
        type: 'data_format_error',
        pattern: /Cannot read property.*of undefined/,
        solution: '检查数据结构,确保必需的属性存在',
        fix: (error, context) => this.fixDataFormatError(error, context)
      },
      {
        type: 'api_timeout',
        pattern: /timeout.*exceeded/i,
        solution: '增加超时时间或优化 API 调用',
        fix: (error, context) => this.fixApiTimeout(error, context)
      },
      {
        type: 'rate_limit',
        pattern: /rate limit|too many requests/i,
        solution: '添加重试机制或减少请求频率',
        fix: (error, context) => this.fixRateLimit(error, context)
      },
      {
        type: 'authentication_error',
        pattern: /unauthorized|authentication.*failed/i,
        solution: '检查凭证配置和权限设置',
        fix: (error, context) => this.fixAuthError(error, context)
      }
    ];
  }

  // 诊断错误
  diagnose(error, executionContext) {
    const diagnosis = {
      errorType: 'unknown',
      possibleCauses: [],
      suggestedFixes: [],
      automaticFix: null
    };

    // 匹配已知问题模式
    for (const issue of this.commonIssues) {
      if (issue.pattern.test(error.message)) {
        diagnosis.errorType = issue.type;
        diagnosis.suggestedFixes.push(issue.solution);
        diagnosis.automaticFix = issue.fix;
        break;
      }
    }

    // 分析执行上下文
    diagnosis.possibleCauses = this.analyzeContext(error, executionContext);

    return diagnosis;
  }

  // 分析执行上下文
  analyzeContext(error, context) {
    const causes = [];

    // 检查数据流
    if (context.inputData && context.inputData.length === 0) {
      causes.push('输入数据为空');
    }

    // 检查节点配置
    if (context.nodeParameters) {
      const requiredParams = this.getRequiredParameters(context.nodeType);
      requiredParams.forEach(param => {
        if (!context.nodeParameters[param]) {
          causes.push(`缺少必需参数: ${param}`);
        }
      });
    }

    // 检查网络连接
    if (error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND') {
      causes.push('网络连接问题');
    }

    return causes;
  }

  // 自动修复数据格式错误
  fixDataFormatError(error, context) {
    const fixes = [];

    // 添加空值检查
    fixes.push({
      type: 'add_null_check',
      code: `
        if (!$json.propertyName) {
          return { json: { error: 'Missing required property' } };
        }
      `
    });

    // 添加默认值
    fixes.push({
      type: 'add_default_value',
      code: `
        const safeValue = $json.propertyName || 'default_value';
      `
    });

    return fixes;
  }
}

// 使用诊断工具
const diagnostic = new WorkflowDiagnostic();

try {
  // 执行可能出错的操作
  const result = await riskyOperation();
} catch (error) {
  const diagnosis = diagnostic.diagnose(error, executionContext);

  console.log('错误诊断:', diagnosis);

  // 尝试自动修复
  if (diagnosis.automaticFix) {
    const fixes = diagnosis.automaticFix(error, executionContext);
    console.log('建议的修复方案:', fixes);
  }
}

调试最佳实践

  1. 渐进式调试:从简单到复杂,逐步缩小问题范围
  2. 数据检查点:在关键节点添加数据检查
  3. 日志记录:记录关键操作和状态变化
  4. 错误重现:创建可重现的测试用例
  5. 版本对比:对比工作和不工作的版本差异

质量保证

质量保证是确保工作流稳定性和可靠性的重要环节。

代码质量检查

工作流质量评估

javascript
// 工作流质量评估工具
class WorkflowQualityAssessment {
  constructor() {
    this.qualityRules = [
      new ComplexityRule(),
      new NamingRule(),
      new ErrorHandlingRule(),
      new PerformanceRule(),
      new SecurityRule()
    ];
  }

  // 评估工作流质量
  assessWorkflow(workflow) {
    const assessment = {
      overallScore: 0,
      maxScore: 100,
      issues: [],
      recommendations: [],
      metrics: {}
    };

    // 执行所有质量规则检查
    this.qualityRules.forEach(rule => {
      const result = rule.evaluate(workflow);
      assessment.issues.push(...result.issues);
      assessment.recommendations.push(...result.recommendations);

      // 更新分数
      assessment.overallScore += result.score;
    });

    // 计算平均分数
    assessment.overallScore = Math.round(assessment.overallScore / this.qualityRules.length);

    // 生成质量报告
    assessment.report = this.generateQualityReport(assessment);

    return assessment;
  }

  // 生成质量报告
  generateQualityReport(assessment) {
    const report = {
      summary: this.getQualitySummary(assessment.overallScore),
      criticalIssues: assessment.issues.filter(i => i.severity === 'critical'),
      improvements: assessment.recommendations.slice(0, 5), // 前5个建议
      nextSteps: this.getNextSteps(assessment)
    };

    return report;
  }

  getQualitySummary(score) {
    if (score >= 90) return '优秀 - 工作流质量很高';
    if (score >= 80) return '良好 - 工作流质量较好,有少量改进空间';
    if (score >= 70) return '一般 - 工作流基本可用,需要一些改进';
    if (score >= 60) return '较差 - 工作流存在较多问题,需要重点改进';
    return '很差 - 工作流存在严重问题,建议重新设计';
  }
}

// 复杂度检查规则
class ComplexityRule {
  evaluate(workflow) {
    const result = {
      score: 100,
      issues: [],
      recommendations: []
    };

    const nodeCount = workflow.nodes.length;
    const connectionCount = workflow.connections.length;
    const maxDepth = this.calculateMaxDepth(workflow);

    // 节点数量检查
    if (nodeCount > 20) {
      result.score -= 20;
      result.issues.push({
        type: 'complexity',
        severity: 'warning',
        message: `工作流节点过多 (${nodeCount}),建议拆分为多个子工作流`
      });
    }

    // 深度检查
    if (maxDepth > 10) {
      result.score -= 15;
      result.issues.push({
        type: 'complexity',
        severity: 'warning',
        message: `工作流层次过深 (${maxDepth}),可能影响可读性`
      });
    }

    return result;
  }

  calculateMaxDepth(workflow) {
    // 计算工作流的最大深度
    const visited = new Set();
    let maxDepth = 0;

    const dfs = (nodeId, depth) => {
      if (visited.has(nodeId)) return;
      visited.add(nodeId);
      maxDepth = Math.max(maxDepth, depth);

      const connections = workflow.connections.filter(c => c.source === nodeId);
      connections.forEach(conn => {
        dfs(conn.target, depth + 1);
      });
    };

    // 从触发器节点开始
    const triggerNodes = workflow.nodes.filter(n => n.type.includes('Trigger'));
    triggerNodes.forEach(node => dfs(node.id, 1));

    return maxDepth;
  }
}

性能测试

负载测试框架

javascript
// 工作流性能测试
class WorkflowPerformanceTester {
  constructor() {
    this.metrics = {
      executionTime: [],
      memoryUsage: [],
      throughput: [],
      errorRate: []
    };
  }

  // 执行负载测试
  async runLoadTest(workflowId, options = {}) {
    const {
      concurrency = 10,
      duration = 60000, // 60秒
      rampUp = 10000    // 10秒渐增
    } = options;

    const testResults = {
      startTime: Date.now(),
      endTime: null,
      totalExecutions: 0,
      successfulExecutions: 0,
      failedExecutions: 0,
      averageResponseTime: 0,
      maxResponseTime: 0,
      minResponseTime: Infinity,
      throughput: 0,
      errors: []
    };

    const workers = [];
    const startTime = Date.now();

    // 创建并发工作者
    for (let i = 0; i < concurrency; i++) {
      const worker = this.createWorker(workflowId, testResults);
      workers.push(worker);

      // 渐增启动
      setTimeout(() => worker.start(), (i * rampUp) / concurrency);
    }

    // 等待测试完成
    await new Promise(resolve => setTimeout(resolve, duration));

    // 停止所有工作者
    workers.forEach(worker => worker.stop());

    // 计算最终结果
    testResults.endTime = Date.now();
    testResults.averageResponseTime = this.calculateAverage(this.metrics.executionTime);
    testResults.maxResponseTime = Math.max(...this.metrics.executionTime);
    testResults.minResponseTime = Math.min(...this.metrics.executionTime);
    testResults.throughput = testResults.totalExecutions / ((testResults.endTime - testResults.startTime) / 1000);

    return testResults;
  }

  // 创建工作者
  createWorker(workflowId, testResults) {
    let running = false;

    return {
      start: () => {
        running = true;
        this.executeWorkflow(workflowId, testResults, () => running);
      },
      stop: () => {
        running = false;
      }
    };
  }

  // 执行工作流
  async executeWorkflow(workflowId, testResults, shouldContinue) {
    while (shouldContinue()) {
      const startTime = Date.now();
      const startMemory = process.memoryUsage().heapUsed;

      try {
        await this.triggerWorkflow(workflowId);

        const executionTime = Date.now() - startTime;
        const memoryUsed = process.memoryUsage().heapUsed - startMemory;

        // 记录指标
        this.metrics.executionTime.push(executionTime);
        this.metrics.memoryUsage.push(memoryUsed);

        testResults.totalExecutions++;
        testResults.successfulExecutions++;

      } catch (error) {
        testResults.totalExecutions++;
        testResults.failedExecutions++;
        testResults.errors.push({
          timestamp: new Date().toISOString(),
          error: error.message
        });
      }

      // 短暂延迟避免过度负载
      await new Promise(resolve => setTimeout(resolve, 100));
    }
  }

  // 生成性能报告
  generatePerformanceReport(testResults) {
    return {
      summary: {
        totalExecutions: testResults.totalExecutions,
        successRate: (testResults.successfulExecutions / testResults.totalExecutions * 100).toFixed(2) + '%',
        averageResponseTime: testResults.averageResponseTime.toFixed(2) + 'ms',
        throughput: testResults.throughput.toFixed(2) + ' req/s'
      },
      performance: {
        responseTime: {
          average: testResults.averageResponseTime,
          min: testResults.minResponseTime,
          max: testResults.maxResponseTime,
          p95: this.calculatePercentile(this.metrics.executionTime, 95),
          p99: this.calculatePercentile(this.metrics.executionTime, 99)
        },
        memory: {
          average: this.calculateAverage(this.metrics.memoryUsage),
          peak: Math.max(...this.metrics.memoryUsage)
        }
      },
      issues: this.identifyPerformanceIssues(testResults),
      recommendations: this.generatePerformanceRecommendations(testResults)
    };
  }

  // 识别性能问题
  identifyPerformanceIssues(testResults) {
    const issues = [];

    if (testResults.averageResponseTime > 5000) {
      issues.push({
        type: 'slow_response',
        severity: 'high',
        message: '平均响应时间过长,可能影响用户体验'
      });
    }

    if (testResults.successfulExecutions / testResults.totalExecutions < 0.95) {
      issues.push({
        type: 'high_error_rate',
        severity: 'critical',
        message: '错误率过高,需要检查工作流稳定性'
      });
    }

    return issues;
  }
}

自动化测试

自动化测试能够提高测试效率,确保代码质量的持续性。

测试自动化框架

持续集成测试

javascript
// 自动化测试套件
class AutomatedTestSuite {
  constructor() {
    this.testRunners = {
      unit: new UnitTestRunner(),
      integration: new IntegrationTestRunner(),
      e2e: new E2ETestRunner(),
      performance: new PerformanceTestRunner()
    };
  }

  // 运行完整测试套件
  async runFullTestSuite(workflowId) {
    const results = {
      startTime: new Date().toISOString(),
      workflowId: workflowId,
      testResults: {},
      overallStatus: 'passed',
      summary: {}
    };

    // 按顺序执行不同类型的测试
    const testTypes = ['unit', 'integration', 'e2e', 'performance'];

    for (const testType of testTypes) {
      console.log(`运行 ${testType} 测试...`);

      try {
        const testResult = await this.testRunners[testType].run(workflowId);
        results.testResults[testType] = testResult;

        if (!testResult.passed) {
          results.overallStatus = 'failed';
        }
      } catch (error) {
        results.testResults[testType] = {
          passed: false,
          error: error.message
        };
        results.overallStatus = 'failed';
      }
    }

    results.endTime = new Date().toISOString();
    results.summary = this.generateTestSummary(results);

    return results;
  }
}

CI/CD 集成

GitHub Actions 配置

yaml
# .github/workflows/n8n-test.yml
name: n8n Workflow Tests

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:13
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: n8n_test

    steps:
    - uses: actions/checkout@v2

    - name: Setup Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '16'
        cache: 'npm'

    - name: Install dependencies
      run: npm ci

    - name: Run unit tests
      run: npm run test:unit

    - name: Run integration tests
      run: npm run test:integration

    - name: Run workflow validation
      run: npm run validate:workflows

小结

测试与调试是确保 n8n 工作流质量的重要手段:

  1. 测试策略:采用分层测试方法,从单元测试到端到端测试
  2. 调试技巧:掌握有效的调试工具和方法
  3. 质量保证:建立完善的质量评估和性能测试机制
  4. 自动化测试:通过 CI/CD 实现持续的质量保证
  5. 问题诊断:快速识别和解决常见问题

记住,测试不是开发完成后的附加工作,而应该贯穿整个开发过程。良好的测试习惯能够帮助我们构建更稳定、更可靠的自动化系统。

下一篇文章我们将进入实战项目部分,学习如何构建企业级的 CRM 自动化系统。