Testland
Browse all skills & agents

aws-sam-local-testing

Wraps AWS SAM (Serverless Application Model) Local CLI for testing Lambda functions locally: `sam local invoke` (single invocation with event payload), `sam local start-api` (local API Gateway emulator), `sam local start-lambda` (local Lambda invoke endpoint for AWS SDK clients), and event-payload generation (`sam local generate-event`). For C#/.NET Lambdas, references/dotnet.md covers handler-direct testing with Amazon.Lambda.TestUtilities (TestLambdaContext, TestLambdaLogger) and the dotnet-lambda CLI. Use when testing Lambda + API Gateway + integrated AWS services locally.

Install with skills.sh (any agent)

npx skills add testland/qa --skill aws-sam-local-testing
View source

aws-sam-local-testing

Overview

AWS SAM Local is the canonical local-testing toolchain for AWS Lambda. Per docs.aws.amazon.com/serverless-application-model (opens in new window), it runs Lambdas in Docker containers locally with images that mirror the Lambda runtime - same Linux, same Node/Python/Java binaries, same handler-invocation contract.

When to use

  • Local unit + integration tests for Lambda handlers.
  • Testing API Gateway + Lambda routing locally.
  • Reproducing prod Lambda behaviour without deploying.
  • Event-payload-driven tests (S3 event, SQS message, API GW request).

Authoring

Install

brew install aws-sam-cli
sam --version            # 1.x or higher
docker --version         # Required for sam local

Project structure

A SAM project has template.yaml declaring Lambda functions:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
  HelloFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: app.handler
      Runtime: python3.12
      Timeout: 10
      MemorySize: 512
      Events:
        Api:
          Type: Api
          Properties:
            Path: /hello
            Method: GET

Generate an event payload

Per SAM docs:

sam local generate-event apigateway aws-proxy --path /hello --method GET > event.json
sam local generate-event s3 put --bucket mybucket --key file.txt > s3-event.json
sam local generate-event sqs receive-message > sqs-event.json

Single invocation

sam local invoke HelloFunction --event event.json

Output: handler's return value, plus the simulated Lambda runtime log lines.

Local API Gateway

sam local start-api --port 3000

Now curl http://localhost:3000/hello exercises the full API Gateway → Lambda routing.

Local Lambda invoke endpoint

sam local start-lambda --port 3001

Then point AWS SDK clients at http://localhost:3001:

import boto3
lambda_client = boto3.client('lambda', endpoint_url='http://localhost:3001', region_name='us-east-1')
lambda_client.invoke(FunctionName='HelloFunction', Payload=b'{}')

Useful for testing Lambda → Lambda invocations end-to-end.

.NET Lambdas - handler-direct testing

For C#/.NET Lambda handlers, prefer in-process handler-direct invocation with Amazon.Lambda.TestUtilities (TestLambdaContext, TestLambdaLogger) over per-test sam local invoke spawns - setup, worked tests, and the dotnet-lambda CLI are in references/dotnet.md.

Integration with pytest

import subprocess, json

def invoke_lambda(name, event):
    proc = subprocess.run(
        ["sam", "local", "invoke", name, "--event", "-"],
        input=json.dumps(event), text=True, capture_output=True,
    )
    return json.loads(proc.stdout)

def test_hello():
    result = invoke_lambda("HelloFunction", {"name": "world"})
    assert result["statusCode"] == 200
    assert "Hello, world" in result["body"]

Running

sam build                # Package Lambdas
sam local invoke HelloFunction --event event.json

For watch-mode:

sam build --use-container --watch

CI integration

jobs:
  sam-local-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: aws-actions/setup-sam@v2
      - run: sam build --use-container
      - run: pytest tests/lambda/

Anti-patterns

Anti-patternWhy it failsFix
Skip sam build between code changesStale package; old code runssam build (or watch mode)
sam local invoke for full-suiteSpawn cost per invocation; slowsam local start-lambda once, invoke many
Compare local timing to prodDocker overhead; cold-start model differs per cold-start-budget-referenceTest correctness locally; latency in prod
No event-payload generationHand-rolled events miss fieldssam local generate-event
Mock AWS SDK calls locallyTests pass but prod IAM / endpoints failUse LocalStack or test against real low-cost AWS account
Skip API Gateway routing testLambda alone passes; API GW integration breakssam local start-api
Hardcoded path in testOS-specificUse generated events

Limitations

  • Docker overhead. Cold starts in SAM Local are 5-15s (Docker container spin-up); not representative of prod cold-start budgets per cold-start-budget-reference.
  • Doesn't test IAM. Local invocations run with your AWS CLI credentials, not the Lambda's role.
  • Doesn't test event-source mapping. SQS / DynamoDB Streams / EventBridge bindings are SAM-template-only locally.
  • VPC + private endpoints can't be simulated. Local Lambdas reach internet directly.
  • Pair with LocalStack for fuller AWS-service emulation (localstack.cloud (opens in new window)).

References

.NET Lambda testing - Amazon.Lambda.TestUtilities and the dotnet-lambda CLI

View source (opens in new window)

.NET Lambda testing - Amazon.Lambda.TestUtilities and the dotnet-lambda CLI

For C#/.NET Lambdas the fast path is handler-direct invocation with a mock context, not spawning sam local invoke per test. AWS's canonical toolkit is aws-lambda-dotnet (opens in new window): Amazon.Lambda.TestUtilities for library-level fakes plus the Mock Lambda Test Tool (opens in new window) UI for manual invocation.

Install

dotnet add package Amazon.Lambda.Core
dotnet add package Amazon.Lambda.Serialization.SystemTextJson
dotnet add package Amazon.Lambda.TestUtilities
dotnet add package Microsoft.NET.Test.Sdk
dotnet add package xunit

Handler + TestLambdaContext

Per Amazon.Lambda.TestUtilities (opens in new window):

using Amazon.Lambda.Core;
using Amazon.Lambda.Serialization.SystemTextJson;

[assembly: LambdaSerializer(typeof(DefaultLambdaJsonSerializer))]

public class Functions
{
    public string Handler(string input, ILambdaContext context)
    {
        context.Logger.LogLine($"Got input: {input}");
        return input.ToUpper();
    }
}
using Amazon.Lambda.TestUtilities;
using Xunit;

[Fact]
public void Handler_Uppercases()
{
    var context = new TestLambdaContext
    {
        FunctionName = "test-fn",
        RemainingTime = TimeSpan.FromSeconds(30),  // Logger auto-set to TestLambdaLogger
    };
    Assert.Equal("HELLO", new Functions().Handler("hello", context));
}

[Fact]
public void Handler_LogsInput()
{
    var context = new TestLambdaContext();
    new Functions().Handler("hi", context);
    var logger = (TestLambdaLogger)context.Logger;
    Assert.Contains("Got input: hi", logger.Buffer.ToString());
}

Remaining-time behaviour

Set RemainingTime low to exercise timeout-aware handlers (the early-return pattern in the cold-start-budget-reference references/timeout-budgets.md):

var context = new TestLambdaContext { RemainingTime = TimeSpan.FromSeconds(3) };
var result = new Functions().Handler("work-that-takes-time", context);
Assert.Contains("partial", result);

Serialised event payloads

var json = File.ReadAllText("Events/apigw-request.json");   // from sam local generate-event
var request = new DefaultLambdaJsonSerializer().Deserialize<APIGatewayProxyRequest>(json);
var response = new Functions().HandleApi(request, new TestLambdaContext());
Assert.Equal(200, response.StatusCode);

dotnet-lambda CLI (deploy + invoke path)

Per aws-extensions-for-dotnet-cli (opens in new window):

dotnet tool install -g Amazon.Lambda.Tools
dotnet lambda invoke-function MyFunction --payload '"hello"'

For local-only testing, prefer handler-direct invocation.

CI

jobs:
  dotnet-lambda-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-dotnet@v4
        with: { dotnet-version: '8.0.x' }
      - run: dotnet restore
      - run: dotnet test --no-build --verbosity normal

Anti-patterns

Anti-patternWhy it failsFix
Spawn dotnet lambda invoke per testNetwork calls; slow; rate-limitedHandler-direct invocation
Skip TestLambdaContextNull context fails at runtimeAlways pass one
RemainingTime = TimeSpan.MaxValueTimeout logic never exercisedRealistic remaining time
Hand-rolled APIGatewayProxyRequestSchema driftsam local generate-event fixture
Missing [assembly: LambdaSerializer]Runtime deserialization failsRegister the serializer in tests too
Mocking the LoggerLoses TestLambdaLogger.Buffer replayUse the default TestLambdaContext logger

Limitations

  • In-process tests use the standard JIT; ReadyToRun / Native AOT Lambdas behave differently - pair with deployed-Lambda tests.
  • The Lambda runtime API (next/response polling) is not exercised.
  • Cold-start behaviour is invisible warm-in-process; see the parent skill's budget tables.

References

Related skills

azure-functions-tests

Runs Azure Functions locally using Azure Functions Core Tools v4 (`func start`), Azurite storage emulation, and framework-native unit tests for handler code (.NET isolated worker model, Node.js v4, Python v2). Covers HTTP, queue, and timer trigger testing, admin-endpoint invocation for non-HTTP triggers, and binding verification via local.settings.json. Use when testing Azure Functions before deployment, reproducing trigger behaviour without live Azure services, or gating function handler logic in CI.

cloudflare-workers-miniflare

Wraps Miniflare 3 (the official Cloudflare Workers simulator) and Wrangler dev for testing Workers locally. Covers Miniflare's getMiniflare() programmatic API (workerd-backed simulation matching prod), the wrangler dev local-mode (live-reload during dev), KV / Durable Objects / R2 / D1 bindings emulation, and Vitest + @cloudflare/vitest-pool-workers for in-process tests. Use when testing Cloudflare Workers code locally.

cold-start-budget-reference

Pure-reference catalog of latency budgets across serverless runtimes: cold starts AND timeouts. Covers AWS Lambda's three-phase cold start (Init: download+unzip+runtime-bootstrap; Init code: imports + module load; Invoke: handler execution), Cloudflare Workers' isolate model (sub-millisecond cold starts via V8 isolates per developers.cloudflare.com), Vercel Edge Runtime, Lambda SnapStart for JVM (snapshot-restore for Java), and provisioned-concurrency trade-offs, plus Lambda timeout + billing budgets in references/timeout-budgets.md (the 15-minute hard limit, getRemainingTimeInMillis, per-ms billing, memory-CPU scaling, the API Gateway 29s / SQS visibility-timeout integration cascade). Use when designing latency or timeout budgets, choosing a runtime, sizing memory, or auditing cold-start variance in production.

serverless-integration-test-builder

Workflow-driven skill that builds the integration-test suite for a serverless application from its IaC definition (SAM template / serverless.yml / Wrangler config / Vercel functions / Netlify functions). Walks through: identifying the function inventory + event sources, picking the right local-emulator per function (sam local / Miniflare / netlify dev / vercel dev / serverless-offline), generating test events per event source, asserting on cold-start + timeout budgets, and emitting the test directory + CI config. Use when introducing integration tests to a serverless project.