CtrlK
BlogDocsLog inGet started
Tessl Logo

testland/netlify-functions-tests

Wraps Netlify Functions testing patterns: Netlify Dev (`netlify dev`) for local routing emulation, the @netlify/functions handler API testing pattern, Netlify Edge Functions (Deno runtime) vs Background Functions (Lambda under the hood) distinction, and scheduled-function (cron) test patterns. Use when testing Netlify Functions or Edge Functions.

75

Quality

94%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files
name:
netlify-functions-tests
description:
Wraps Netlify Functions testing patterns: Netlify Dev (`netlify dev`) for local routing emulation, the @netlify/functions handler API testing pattern, Netlify Edge Functions (Deno runtime) vs Background Functions (Lambda under the hood) distinction, and scheduled-function (cron) test patterns. Use when testing Netlify Functions or Edge Functions.

netlify-functions-tests

Overview

Netlify Functions come in three flavors:

  1. Standard Functions - Node/Go, Lambda under the hood, 10s default timeout (background functions get 15min)
  2. Background Functions - async Lambda, 15min max
  3. Edge Functions - Deno runtime, 30s timeout

Per docs.netlify.com/functions, the local emulator (netlify dev) runs all three.

When to use

  • Tests for Netlify Functions (any flavor).
  • Local routing emulation matching prod redirects + headers.
  • Edge Functions testing (Deno-runtime constraints).

Authoring

Install

npm install -g netlify-cli
npm install --save-dev @netlify/functions @netlify/edge-functions

Standard Function

// netlify/functions/hello.ts
import type { Handler } from '@netlify/functions';

export const handler: Handler = async (event) => {
  return {
    statusCode: 200,
    body: JSON.stringify({ message: 'Hello' }),
  };
};

Test the handler directly

import { handler } from '../netlify/functions/hello';

test('hello returns 200', async () => {
  const event: any = {
    httpMethod: 'GET',
    path: '/.netlify/functions/hello',
    headers: {},
    queryStringParameters: {},
    body: null,
  };
  const result = await handler(event, {} as any, () => {});
  expect(result.statusCode).toBe(200);
  expect(JSON.parse(result.body)).toEqual({ message: 'Hello' });
});

netlify dev (local routing)

netlify dev --port 8888

Now curl http://localhost:8888/.netlify/functions/hello hits the function exactly as on netlify.app. Redirects + rewrites defined in netlify.toml are honored.

Background Functions (long-running)

Per docs.netlify.com: filename must end in -background.ts:

// netlify/functions/process-background.ts
import type { Handler } from '@netlify/functions';

export const handler: Handler = async (event) => {
  // 15min budget per lambda-timeout-budget-reference
  await doLongRunningWork(event.body);
  return { statusCode: 200 };
};

These return 202 immediately to the caller; work continues in the background.

Edge Functions (Deno)

Per docs.netlify.com/edge-functions:

// netlify/edge-functions/middleware.ts
import type { Context } from '@netlify/edge-functions';

export default async (request: Request, context: Context) => {
  return new Response('Hello from Edge', {
    headers: { 'content-type': 'text/plain' },
  });
};

Test with deno test or via netlify dev (which spins up the Deno runtime).

Scheduled (cron) Functions

// netlify/functions/cleanup.ts
import type { Config } from '@netlify/functions';

export const handler = async () => {
  await doCleanup();
  return { statusCode: 200 };
};

export const config: Config = {
  schedule: '@daily',  // cron expression also accepted
};

Test by invoking the handler directly; the schedule itself is Netlify-platform-driven.

Running

npx jest                  # Handler-direct tests
netlify dev               # Full local emulator

CI integration

jobs:
  netlify-functions-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v4
      - run: npm ci
      - run: npx jest

For deployed-function smoke tests, use the netlify deploy --build --dir=public then run against the preview URL.

Anti-patterns

Anti-patternWhy it failsFix
Standard Function with > 10s workNetlify times out at 10s by defaultUse Background Function (15min)
Test handler with empty event objectHandler reads event.headers / event.body → crashProvide a complete event
Edge Function importing Node modulesDeno runtime; failsUse Deno-compatible imports (npm: + jsr: specifiers)
Skip netlify dev for routing testsMisses redirects + rewritesAlways use netlify dev for routing
Background Function with sync response expectationReturns 202 immediately; caller's "await result" gets nothingUse async return pattern
Hardcoded function path in testsNetlify paths are /.netlify/functions/{name}; client paths via redirectUse the rewrite target
Scheduled-function test that asserts on the schedule itselfThe schedule is platform-controlledTest handler invocation; trust the schedule

Limitations

  • Edge Functions run on Deno; standard ones on Node. Different module systems; different stdlib.
  • Background Functions return 202 not the actual response. Callers must poll a status endpoint or webhook.
  • netlify dev may differ from prod in subtle areas (geo headers, IP-based context).
  • Cold-start budget per cold-start-budget-reference. Standard Functions: ~300ms-2s (Lambda-equivalent).
  • No A/B-testing primitives. Use other tools (LD / Statsig / GrowthBook).

References

Workspace
testland
Visibility
Public
Created
Last updated
Publish Source
GitHub
Badge
testland/netlify-functions-tests badge