Modern QA2026AWS Lambda with SAM Local — tiles
Log inJoin
43 / 75 · 08 Infrastructure as Code Testing · Serverless Function Testing← prev⊞ allnext →☰ Read as one page

8.2AWS Lambda with SAM Local

AWS SAM (Serverless Application Model) provides local emulation for Lambda functions. SAM Local runs a Docker container that mimics the Lambda execution environment, allowing you to test functions locally before deploying to AWS.

Local Invocation

# Invoke a single function locally with a test event
sam local invoke MyFunction --event events/api-gateway.json

# Start a local API Gateway emulator
sam local start-api --port 3000

# Run tests against the local emulator
pytest tests/integration/ --base-url http://localhost:3000

# Generate a sample event for testing
sam local generate-event apigateway aws-proxy > events/api-gateway.json
sam local generate-event sqs receive-message > events/sqs-message.json
sam local generate-event s3 put > events/s3-put.json

Unit Testing Lambda Handlers

Lambda handlers are just functions. Test them like any other function, mocking the AWS services they call:

# tests/unit/test_handler.py
import json
import pytest
from unittest.mock import patch, MagicMock
from src.handlers.order_processor import lambda_handler

def test_order_processing_success():
    """Test successful order processing."""
    event = {
        "Records": [{
            "body": json.dumps({
                "orderId": "ORD-123",
                "items": [{"sku": "WIDGET-A", "qty": 2}],
                "customerId": "CUST-456"
            })
        }]
    }
    context = MagicMock()
    context.function_name = "order-processor"
    context.memory_limit_in_mb = 256
    context.get_remaining_time_in_millis.return_value = 30000

    result = lambda_handler(event, context)

    assert result["statusCode"] == 200
    body = json.loads(result["body"])
    assert body["orderId"] == "ORD-123"
    assert body["status"] == "processed"

def test_order_processing_invalid_payload():
    """Test handling of malformed input."""
    event = {"Records": [{"body": "not json"}]}
    context = MagicMock()

    result = lambda_handler(event, context)

    assert result["statusCode"] == 400
    body = json.loads(result["body"])
    assert "error" in body

def test_order_processing_missing_required_fields():
    """Test handling of missing required fields."""
    event = {
        "Records": [{
            "body": json.dumps({
                "orderId": "ORD-123"
                # Missing: items and customerId
            })
        }]
    }
    context = MagicMock()

    result = lambda_handler(event, context)

    assert result["statusCode"] == 400

@patch("src.handlers.order_processor.dynamodb_client")
def test_order_persisted_to_dynamodb(mock_dynamo):
    """Test that processed orders are saved to DynamoDB."""
    event = {
        "Records": [{
            "body": json.dumps({
                "orderId": "ORD-123",
                "items": [{"sku": "WIDGET-A", "qty": 2}],
                "customerId": "CUST-456"
            })
        }]
    }
    context = MagicMock()

    lambda_handler(event, context)

    mock_dynamo.put_item.assert_called_once()
    call_args = mock_dynamo.put_item.call_args
    item = call_args["Item"]
    assert item["orderId"]["S"] == "ORD-123"
    assert item["status"]["S"] == "processed"