Smithery Logo
MCPsSkillsDocsPricing
Login
NewFlame, an assistant that learns and improves. Available onTelegramSlack
    proffesor-for-testing

    n8n-trigger-testing-strategies

    proffesor-for-testing/n8n-trigger-testing-strategies
    Coding
    158

    About

    SKILL.md

    Install

    • Telegram
      Telegram
    • Slack
      Slack
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    • Download skill
    ├─
    ├─
    └─
    Smithery Logo

    Give agents more agency

    Resources

    DocumentationPrivacy PolicySystem Status

    Company

    PricingAboutBlog

    Connect

    © 2026 Smithery. All rights reserved.

    About

    Webhook testing, schedule validation, event-driven triggers, and polling mechanism testing for n8n workflows. Use when testing how workflows are triggered.

    SKILL.md

    n8n Trigger Testing Strategies

    When testing n8n triggers: 1. IDENTIFY trigger type (webhook, schedule, polling, event) 2. TEST with various valid payloads 3. VERIFY authentication and authorization 4. CHECK error handling for invalid inputs 5. MEASURE response time and reliability

    Quick Trigger Checklist:

    • Trigger activates workflow correctly
    • Payload parsed and validated
    • Authentication enforced (if configured)
    • Error responses are informative
    • Response time is acceptable

    Critical Success Factors:

    • Test edge cases (empty payloads, large payloads)
    • Verify idempotency where needed
    • Check timeout handling
    • Monitor for missed triggers

    Quick Reference Card

    n8n Trigger Types

    Type Use Case Testing Focus
    Webhook External HTTP calls Payloads, auth, methods
    Schedule Timed execution Cron accuracy, timezone
    Polling Check for changes Interval, deduplication
    Event Service events Event handling, filtering

    Common Webhook Configurations

    Setting Options Impact
    HTTP Method GET, POST, PUT, DELETE Request handling
    Authentication None, Basic, Header Security
    Response Mode Immediately, Last Node, Custom Response timing
    Path Custom URL path Endpoint identification

    Webhook Testing

    Basic Webhook Test

    // Test webhook with various payloads
    async function testWebhook(webhookUrl: string): Promise<WebhookTestResult> {
      const testPayloads = [
        // Valid JSON
        { type: 'json', data: { event: 'test', timestamp: Date.now() } },
        // Empty object
        { type: 'empty', data: {} },
        // Large payload
        { type: 'large', data: { items: Array(1000).fill({ id: 1, name: 'test' }) } },
        // Nested data
        { type: 'nested', data: { level1: { level2: { level3: { value: 'deep' } } } } },
        // Special characters
        { type: 'special', data: { text: 'Hello <script>alert("xss")</script>' } }
      ];
    
      const results: PayloadTestResult[] = [];
    
      for (const payload of testPayloads) {
        const startTime = Date.now();
    
        try {
          const response = await fetch(webhookUrl, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(payload.data)
          });
    
          results.push({
            payloadType: payload.type,
            success: response.ok,
            status: response.status,
            responseTime: Date.now() - startTime,
            responseBody: await response.text()
          });
        } catch (error) {
          results.push({
            payloadType: payload.type,
            success: false,
            error: error.message
          });
        }
      }
    
      return { webhookUrl, results };
    }
    

    HTTP Method Testing

    // Test all HTTP methods
    async function testWebhookMethods(webhookUrl: string): Promise<MethodTestResult[]> {
      const methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];
      const results: MethodTestResult[] = [];
    
      for (const method of methods) {
        try {
          const response = await fetch(webhookUrl, {
            method,
            headers: { 'Content-Type': 'application/json' },
            body: ['GET', 'HEAD', 'OPTIONS'].includes(method) ? undefined : '{}'
          });
    
          results.push({
            method,
            allowed: response.ok || response.status !== 405,
            status: response.status,
            statusText: response.statusText
          });
        } catch (error) {
          results.push({
            method,
            allowed: false,
            error: error.message
          });
        }
      }
    
      return results;
    }
    

    Authentication Testing

    // Test webhook authentication
    async function testWebhookAuth(webhookUrl: string, authConfig: AuthConfig): Promise<AuthTestResult> {
      const scenarios = [
        // No auth
        { name: 'no-auth', headers: {} },
        // Invalid auth
        { name: 'invalid-auth', headers: { 'Authorization': 'Bearer invalid-token' } },
        // Valid auth
        { name: 'valid-auth', headers: { 'Authorization': `Bearer ${authConfig.token}` } },
        // Expired auth
        { name: 'expired-auth', headers: { 'Authorization': `Bearer ${authConfig.expiredToken}` } }
      ];
    
      const results: AuthScenarioResult[] = [];
    
      for (const scenario of scenarios) {
        const response = await fetch(webhookUrl, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            ...scenario.headers
          },
          body: '{}'
        });
    
        results.push({
          scenario: scenario.name,
          status: response.status,
          authenticated: response.ok,
          errorMessage: response.ok ? null : await response.text()
        });
      }
    
      return {
        authRequired: !results.find(r => r.scenario === 'no-auth')?.authenticated,
        invalidRejected: !results.find(r => r.scenario === 'invalid-auth')?.authenticated,
        validAccepted: results.find(r => r.scenario === 'valid-auth')?.authenticated,
        results
      };
    }
    

    Schedule Testing

    Cron Expression Validation

    // Validate cron expression
    function validateCronExpression(expression: string): CronValidationResult {
      const parts = expression.trim().split(/\s+/);
    
      if (parts.length < 5 || parts.length > 6) {
        return {
          valid: false,
          error: `Expected 5-6 parts, got ${parts.length}`
        };
      }
    
      const [minute, hour, dayOfMonth, month, dayOfWeek, year] = parts;
    
      const validations = [
        { field: 'minute', value: minute, range: [0, 59] },
        { field: 'hour', value: hour, range: [0, 23] },
        { field: 'dayOfMonth', value: dayOfMonth, range: [1, 31] },
        { field: 'month', value: month, range: [1, 12] },
        { field: 'dayOfWeek', value: dayOfWeek, range: [0, 7] }
      ];
    
      for (const v of validations) {
        const result = validateCronField(v.value, v.range);
        if (!result.valid) {
          return { valid: false, error: `Invalid ${v.field}: ${result.error}` };
        }
      }
    
      return {
        valid: true,
        description: describeCronExpression(expression),
        nextExecutions: getNextCronExecutions(expression, 5)
      };
    }
    
    // Get human-readable description
    function describeCronExpression(expression: string): string {
      // Common patterns
      const patterns: Record<string, string> = {
        '* * * * *': 'Every minute',
        '*/5 * * * *': 'Every 5 minutes',
        '0 * * * *': 'Every hour',
        '0 0 * * *': 'Every day at midnight',
        '0 9 * * 1-5': 'Weekdays at 9:00 AM',
        '0 0 1 * *': 'First day of every month',
        '0 0 * * 0': 'Every Sunday at midnight'
      };
    
      return patterns[expression] || 'Custom schedule';
    }
    
    // Calculate next execution times
    function getNextCronExecutions(expression: string, count: number): Date[] {
      const executions: Date[] = [];
      let current = new Date();
    
      // Simple implementation - use cron-parser library in production
      for (let i = 0; i < count; i++) {
        const next = calculateNextCronExecution(expression, current);
        executions.push(next);
        current = new Date(next.getTime() + 60000); // Move past this execution
      }
    
      return executions;
    }
    

    Schedule Reliability Testing

    // Test schedule trigger reliability
    async function testScheduleReliability(triggerId: string, testDuration: number): Promise<ScheduleTestResult> {
      const startTime = Date.now();
      const expectedExecutions: Date[] = [];
      const actualExecutions: Date[] = [];
    
      // Calculate expected execution times
      const cronExpression = await getTriggerCronExpression(triggerId);
      let checkTime = new Date(startTime);
      while (checkTime.getTime() < startTime + testDuration) {
        const nextExec = calculateNextCronExecution(cronExpression, checkTime);
        if (nextExec.getTime() < startTime + testDuration) {
          expectedExecutions.push(nextExec);
        }
        checkTime = new Date(nextExec.getTime() + 60000);
      }
    
      // Monitor actual executions
      const executionListener = onExecutionStart(triggerId, (exec) => {
        actualExecutions.push(new Date(exec.startedAt));
      });
    
      // Wait for test duration
      await sleep(testDuration);
      executionListener.stop();
    
      // Compare expected vs actual
      const comparison = compareExecutions(expectedExecutions, actualExecutions);
    
      return {
        testDuration,
        expectedCount: expectedExecutions.length,
        actualCount: actualExecutions.length,
        missedExecutions: comparison.missed,
        extraExecutions: comparison.extra,
        timingAccuracy: comparison.timingAccuracy,
        reliability: (actualExecutions.length / expectedExecutions.length) * 100
      };
    }
    

    Polling Trigger Testing

    // Test polling trigger behavior
    async function testPollingTrigger(triggerId: string, testConfig: PollingTestConfig): Promise<PollingTestResult> {
      const { interval, testDuration, simulateDataChanges } = testConfig;
    
      const pollEvents: PollEvent[] = [];
      const triggeredExecutions: Execution[] = [];
    
      // Monitor polling events
      const pollListener = onPoll(triggerId, (event) => {
        pollEvents.push({
          timestamp: new Date(),
          dataFound: event.hasNewData,
          itemCount: event.items?.length || 0
        });
      });
    
      // Monitor triggered executions
      const execListener = onExecutionStart(triggerId, (exec) => {
        triggeredExecutions.push(exec);
      });
    
      // Optionally simulate data changes
      if (simulateDataChanges) {
        for (const change of simulateDataChanges) {
          setTimeout(() => {
            injectTestData(triggerId, change.data);
          }, change.at);
        }
      }
    
      // Wait for test duration
      await sleep(testDuration);
    
      pollListener.stop();
      execListener.stop();
    
      // Analyze results
      const expectedPolls = Math.floor(testDuration / interval);
      const actualPolls = pollEvents.length;
    
      return {
        interval,
        testDuration,
        expectedPolls,
        actualPolls,
        pollAccuracy: (actualPolls / expectedPolls) * 100,
        averageInterval: calculateAverageInterval(pollEvents),
        executionsTriggered: triggeredExecutions.length,
        deduplicationWorking: checkDeduplication(triggeredExecutions),
        pollEvents
      };
    }
    
    // Check if deduplication is working
    function checkDeduplication(executions: Execution[]): boolean {
      const processedIds = new Set();
    
      for (const exec of executions) {
        const itemIds = exec.data?.resultData?.runData?.Trigger?.[0]?.data?.main?.[0]
          ?.map(item => item.json?.id);
    
        if (itemIds) {
          for (const id of itemIds) {
            if (processedIds.has(id)) {
              return false; // Duplicate found
            }
            processedIds.add(id);
          }
        }
      }
    
      return true;
    }
    

    Event Trigger Testing

    // Test event-driven triggers
    async function testEventTrigger(triggerId: string, eventConfig: EventTestConfig): Promise<EventTestResult> {
      const { eventType, testEvents, timeout } = eventConfig;
    
      const results: EventResult[] = [];
    
      for (const testEvent of testEvents) {
        // Emit test event
        const startTime = Date.now();
        await emitTestEvent(eventType, testEvent.payload);
    
        // Wait for trigger
        try {
          const execution = await waitForTrigger(triggerId, timeout);
    
          results.push({
            eventType: testEvent.type,
            triggered: true,
            latency: Date.now() - startTime,
            payloadReceived: execution.data?.inputData
          });
        } catch (error) {
          results.push({
            eventType: testEvent.type,
            triggered: false,
            error: error.message
          });
        }
      }
    
      return {
        eventType,
        testsRun: testEvents.length,
        triggered: results.filter(r => r.triggered).length,
        averageLatency: average(results.filter(r => r.triggered).map(r => r.latency)),
        results
      };
    }
    

    Trigger Response Testing

    // Test trigger response modes
    async function testTriggerResponses(webhookUrl: string): Promise<ResponseTestResult> {
      // Test immediate response
      const immediateStart = Date.now();
      const immediateResponse = await fetch(webhookUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: '{"test": "immediate"}'
      });
      const immediateTime = Date.now() - immediateStart;
    
      // Test with workflow execution
      const workflowStart = Date.now();
      const workflowResponse = await fetch(`${webhookUrl}?waitForResponse=true`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: '{"test": "workflow"}'
      });
      const workflowTime = Date.now() - workflowStart;
    
      return {
        immediateResponse: {
          status: immediateResponse.status,
          time: immediateTime,
          body: await immediateResponse.text()
        },
        workflowResponse: {
          status: workflowResponse.status,
          time: workflowTime,
          body: await workflowResponse.text()
        },
        responseMode: workflowTime > immediateTime + 100 ? 'workflow' : 'immediate'
      };
    }
    

    Test Scenarios

    Webhook Scenarios:
      - name: Valid JSON POST
        method: POST
        payload: {"event": "test"}
        expected: 200 OK
    
      - name: Invalid JSON
        method: POST
        payload: "not valid json"
        expected: 400 Bad Request
    
      - name: Missing auth
        method: POST
        headers: {}
        expected: 401 Unauthorized
    
      - name: Large payload
        method: POST
        payload: [10MB of data]
        expected: 413 Payload Too Large
    
    Schedule Scenarios:
      - name: Every 5 minutes
        cron: "*/5 * * * *"
        verify: 12 executions per hour
    
      - name: Weekdays at 9 AM
        cron: "0 9 * * 1-5"
        verify: 5 executions per week
    
    Polling Scenarios:
      - name: 1 minute interval
        interval: 60000
        verify: ~60 polls per hour
    
      - name: Deduplication
        interval: 60000
        inject_duplicate: true
        verify: No duplicate processing
    

    Related Skills

    • n8n-workflow-testing-fundamentals
    • n8n-integration-testing-patterns
    • n8n-security-testing

    Remember

    n8n triggers are the entry points to workflows. Testing requires:

    • Webhook: Payload handling, auth, HTTP methods
    • Schedule: Cron accuracy, timezone handling
    • Polling: Interval accuracy, deduplication
    • Event: Event handling, filtering

    Key patterns: Test with various payloads (valid, invalid, edge cases). Verify authentication enforcement. Check response times and reliability over time.

    Recommended Servers
    bugAgent
    bugAgent
    Browser tool
    Browser tool
    vastlint - IAB XML VAST validator and linter
    vastlint - IAB XML VAST validator and linter
    Repository
    proffesor-for-testing/agentic-qe
    Files