n8n-expression-testing

n8n Expression Testing

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "n8n-expression-testing" with this command: npx skills add proffesor-for-testing/agentic-qe/proffesor-for-testing-agentic-qe-n8n-expression-testing

n8n Expression Testing

<default_to_action> When testing n8n expressions:

  • VALIDATE syntax before execution

  • TEST with multiple context scenarios

  • CHECK for null/undefined handling

  • VERIFY type safety

  • SCAN for security vulnerabilities

Quick Expression Checklist:

  • Valid JavaScript syntax

  • Context variables properly referenced ($json, $node)

  • Null-safe access patterns (?., ??)

  • No dangerous functions (eval, Function)

  • Efficient for large data sets

Common Pitfalls:

  • Accessing nested properties without null checks

  • Type coercion issues

  • Missing fallback values

  • Inefficient array operations </default_to_action>

Quick Reference Card

n8n Expression Syntax

Pattern Example Description

Basic access {{ $json.field }}

Access JSON field

Nested access {{ $json.user.email }}

Access nested property

Array access {{ $json.items[0] }}

Access array element

Node reference {{ $node["Name"].json.id }}

Access other node's data

Method call {{ $json.name.toLowerCase() }}

Call string method

Conditional {{ $json.x ? "yes" : "no" }}

Ternary expression

Context Variables

Variable Description Example

$json

Current item data {{ $json.email }}

$node["Name"]

Other node's data {{ $node["HTTP"].json.body }}

$items()

Multiple items {{ $items("Node", 0, 0).json }}

$now

Current timestamp {{ $now.toISO() }}

$today

Today's date {{ $today }}

$runIndex

Run iteration {{ $runIndex }}

$workflow

Workflow info {{ $workflow.name }}

Expression Syntax Patterns

Safe Data Access

// BAD: Can fail if nested objects are null {{ $json.user.profile.email }}

// GOOD: Optional chaining with fallback {{ $json.user?.profile?.email ?? '' }}

// BAD: Array access without bounds check {{ $json.items[0].name }}

// GOOD: Safe array access {{ $json.items?.[0]?.name ?? 'No items' }}

Type Conversions

// String to Number {{ parseInt($json.quantity, 10) }} {{ parseFloat($json.price) }} {{ Number($json.value) }}

// Number to String {{ String($json.id) }} {{ $json.amount.toString() }} {{ $json.count.toFixed(2) }}

// Date handling {{ new Date($json.timestamp).toISOString() }} {{ DateTime.fromISO($json.date).toFormat('yyyy-MM-dd') }}

// Boolean conversion {{ Boolean($json.active) }} {{ $json.enabled === 'true' }}

String Operations

// Case conversion {{ $json.name.toLowerCase() }} {{ $json.name.toUpperCase() }} {{ $json.name.charAt(0).toUpperCase() + $json.name.slice(1) }}

// String manipulation {{ $json.text.trim() }} {{ $json.text.replace(/\s+/g, ' ') }} {{ $json.text.substring(0, 100) }}

// Template strings {{ Hello, ${$json.firstName} ${$json.lastName}! }} {{ Order #${$json.orderId} - ${$json.status} }}

Array Operations

// Mapping {{ $json.items.map(item => item.name) }} {{ $json.items.map(item => ({ id: item.id, total: item.price * item.qty })) }}

// Filtering {{ $json.items.filter(item => item.active) }} {{ $json.items.filter(item => item.price > 100) }}

// Reducing {{ $json.items.reduce((sum, item) => sum + item.price, 0) }} {{ $json.items.reduce((acc, item) => ({ ...acc, [item.id]: item }), {}) }}

// Finding {{ $json.items.find(item => item.id === $json.targetId) }} {{ $json.items.findIndex(item => item.name === 'target') }}

// Joining {{ $json.tags.join(', ') }} {{ $json.items.map(i => i.name).join(' | ') }}

Validation Patterns

// Validate expression syntax function validateExpressionSyntax(expression: string): ValidationResult { // Remove n8n template markers const code = expression.replace(/{{|}}/g, '').trim();

try { // Check if valid JavaScript new Function(return (${code})); return { valid: true }; } catch (error) { return { valid: false, error: error.message, suggestion: suggestFix(error.message, code) }; } }

// Validate context variables function validateContextVariables(expression: string): string[] { const contextVars = ['$json', '$node', '$items', '$now', '$today', '$runIndex', '$workflow']; const usedVars = []; const invalidVars = [];

// Find all $ prefixed variables const varPattern = /$\w+/g; let match;

while ((match = varPattern.exec(expression)) !== null) { const varName = match[0]; if (contextVars.some(cv => varName.startsWith(cv))) { usedVars.push(varName); } else { invalidVars.push(varName); } }

return { usedVars, invalidVars }; }

// Test expression with sample data function testExpression(expression: string, context: any): TestResult { const code = expression.replace(/{{|}}/g, '').trim();

try { // Create function with context const fn = new Function('$json', '$node', '$items', '$now', '$today', return (${code}));

const result = fn(
  context.$json || {},
  context.$node || {},
  context.$items || (() => ({})),
  context.$now || new Date(),
  context.$today || new Date()
);

return { success: true, result };

} catch (error) { return { success: false, error: error.message }; } }

Common Errors and Fixes

Undefined Property Access

// ERROR: Cannot read property 'email' of undefined {{ $json.user.email }}

// FIX 1: Optional chaining {{ $json.user?.email }}

// FIX 2: With fallback {{ $json.user?.email ?? 'no-email@example.com' }}

// FIX 3: Conditional {{ $json.user ? $json.user.email : '' }}

Type Errors

// ERROR: toLowerCase is not a function (when null) {{ $json.name.toLowerCase() }}

// FIX: Null check first {{ $json.name?.toLowerCase() ?? '' }}

// ERROR: toFixed is not a function (string instead of number) {{ $json.price.toFixed(2) }}

// FIX: Parse as number first {{ parseFloat($json.price).toFixed(2) }}

// ERROR: map is not a function (not an array) {{ $json.items.map(i => i.name) }}

// FIX: Ensure array {{ (Array.isArray($json.items) ? $json.items : []).map(i => i.name) }}

Node Reference Errors

// ERROR: Node "Previous Node" not found {{ $node["Previous Node"].json.data }}

// FIX: Use exact node name (case-sensitive) {{ $node["Previous Node1"].json.data }}

// FIX: Add fallback for safety {{ $node["Previous Node"]?.json?.data ?? {} }}

Security Patterns

Dangerous Functions to Avoid

// DANGEROUS: Never use eval {{ eval($json.code) }}

// DANGEROUS: Dynamic function creation {{ new Function($json.code)() }}

// DANGEROUS: setTimeout with string {{ setTimeout($json.code, 1000) }}

// SAFE: Use explicit operations instead {{ $json.value * 2 }} {{ JSON.parse($json.jsonString) }}

Input Validation

// Validate email format {{ /^[^\s@]+@[^\s@]+.[^\s@]+$/.test($json.email) ? $json.email : '' }}

// Sanitize for HTML (basic) {{ $json.text.replace(/[<>&"']/g, c => ({ '<': '&lt;', '>': '&gt;', '&': '&amp;', '"': '&quot;', "'": '&#39;' }[c])) }}

// Limit string length {{ $json.input.substring(0, 1000) }}

// Validate number range {{ Math.min(Math.max(parseInt($json.value), 0), 100) }}

Performance Optimization

Efficient Array Operations

// SLOW: Multiple iterations {{ $json.items.filter(i => i.active).map(i => i.name).join(', ') }}

// FASTER: Single reduce {{ $json.items.reduce((acc, i) => i.active ? (acc ? ${acc}, ${i.name} : i.name) : acc, '') }}

// SLOW: Nested loops {{ $json.items.map(i => $json.categories.find(c => c.id === i.categoryId)) }}

// FASTER: Create lookup map first (in Code node) const categoryMap = Object.fromEntries($json.categories.map(c => [c.id, c])); return $json.items.map(i => categoryMap[i.categoryId]);

Avoid in Expressions

// AVOID: Complex logic in expressions {{ $json.items.reduce((acc, item) => { const category = $json.categories.find(c => c.id === item.catId); if (category && category.active) { acc.push({ ...item, categoryName: category.name }); } return acc; }, []) }}

// BETTER: Move to Code node for complex transformations

Testing Patterns

// Expression test suite const expressionTests = [ { name: 'Basic property access', expression: '{{ $json.name }}', context: { $json: { name: 'John' } }, expected: 'John' }, { name: 'Nested with optional chaining', expression: '{{ $json.user?.email ?? "default" }}', context: { $json: { user: null } }, expected: 'default' }, { name: 'Array mapping', expression: '{{ $json.items.map(i => i.id).join(",") }}', context: { $json: { items: [{ id: 1 }, { id: 2 }] } }, expected: '1,2' }, { name: 'Conditional expression', expression: '{{ $json.score >= 70 ? "Pass" : "Fail" }}', context: { $json: { score: 85 } }, expected: 'Pass' }, { name: 'Node reference', expression: '{{ $node["Previous"].json.result }}', context: { $node: { Previous: { json: { result: 'success' } } } }, expected: 'success' } ];

// Run tests for (const test of expressionTests) { const result = testExpression(test.expression, test.context); console.log(${test.name}: ${result.result === test.expected ? 'PASS' : 'FAIL'}); }

Agent Coordination

Memory Namespace

aqe/n8n/expressions/ ├── validations/* - Expression validation results ├── patterns/* - Discovered expression patterns ├── errors/* - Common error catalog └── optimizations/* - Performance suggestions

Fleet Coordination

// Coordinate expression validation with workflow testing await Task("Validate expressions", { workflowId: "wf-123", validateAll: true, testWithSampleData: true }, "n8n-expression-validator");

Related Skills

  • n8n-workflow-testing-fundamentals - Workflow testing

  • n8n-security-testing - Security validation

Remember

n8n expressions are JavaScript-like with special context variables ($json, $node, etc.). Testing requires:

  • Syntax validation

  • Context variable verification

  • Null safety checks

  • Type compatibility

  • Security scanning

Key patterns: Use optional chaining (?. ) and nullish coalescing (?? ) for safety. Move complex logic to Code nodes. Always test with edge cases (null, undefined, empty arrays).

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

api-testing-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

compatibility-testing

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

regression-testing

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

test-automation-strategy

No summary provided by upstream source.

Repository SourceNeeds Review