DEV Community

Cover image for Test Applications In Development Environments | πŸ—οΈ Build A Testing Toolkit

Test Applications In Development Environments | πŸ—οΈ Build A Testing Toolkit

Exam Guide: Developer - Associate
πŸ—οΈ Domain 3: Deployment
πŸ“˜ Task 2: Test Applications In Development Environments

Testing on AWS isn't just about unit tests. You need to test locally with SAM CLI, write integration tests against deployed stages, mock AWS services and external APIs, validate event-driven flows, and use API Gateway stages for environment isolation.


πŸ“˜Concepts

The Testing Pyramid

Level Speed Scope AWS Services Tools
Unit Tests Fast (ms) Single function, no external calls Mocked pytest, moto, unittest.mock
Integration Tests Medium (seconds) Multiple services working together Real (deployed) pytest + requests, deployed API Gateway stage
End-to-end Tests Slow (seconds–minutes) Full user workflow Real (deployed) Selenium, Postman, curl against live API

πŸ’‘ Unit tests use mocks (moto for AWS, unittest.mock for external APIs). Integration tests run against real deployed resources.

SAM Local Testing Capabilities

Command What It Does When to Use
sam local invoke Runs a single Lambda function locally in a Docker container Testing individual function logic with a specific event
sam local start-api Starts a local API Gateway emulator Testing full API request/response flow locally
sam local start-lambda Starts a local Lambda endpoint for SDK calls Testing code that invokes Lambda via the SDK
sam local generate-event Generates sample event payloads for AWS services Creating test events for S3, SQS, API Gateway, etc.

⚠️ Key Details:

  • SAM local uses Docker to simulate the Lambda runtime environment
  • It reads your template.yaml to understand function configuration
  • Environment variables can be overridden with --env-vars env.json
  • It does not mock AWS services, therefore if your function calls DynamoDB, it calls real DynamoDB (or you need moto)

πŸ’‘ sam local invoke runs a function once and exits. sam local start-api keeps running and accepts HTTP requests. Both use Docker containers that match the Lambda runtime. Know that SAM local does NOT mock AWS services. Your function still needs real credentials or mocked services.

Mocking Strategies

What to Mock Tool How It Works
AWS Services (DynamoDB, S3, SQS, etc.) moto (@mock_aws) Intercepts boto3 calls and returns realistic responses
External HTTP APIs unittest.mock (@patch) Replaces the HTTP client with a mock that returns controlled responses
Environment Variables os.environ or monkeypatch Set test values for TABLE_NAME, STAGE, etc.
Lambda Context Custom object Create a simple object with function_name, memory_limit_in_mb, etc.

API Gateway Stages for Environment Isolation

Stage URL Pattern Use Case
dev https://{api-id}.execute-api.{region}.amazonaws.com/dev Development and local integration testing
staging https://{api-id}.execute-api.{region}.amazonaws.com/staging QA and pre-production testing
prod https://{api-id}.execute-api.{region}.amazonaws.com/prod Production traffic

Each stage can have its own:

  • Stage Variables: different table names, feature flags, log levels per stage
  • Throttling Settings: different rate limits per stage
  • Logging Configuration: verbose logging in dev, errors only in prod

πŸ’‘ Stage variables are accessible in Lambda via event['stageVariables']. They let you point different stages at different backends (different DynamoDB tables, different Lambda aliases) without changing code.

Event-Driven Testing Approaches

Approach What It Tests How
SAM Local With Generated Events Function logic with realistic payloads sam local generate-event β†’ sam local invoke
Console Test Events Function logic in the deployed environment Create test events in the Lambda console
EventBridge Test Events Rule matching and target invocation Send test events via the EventBridge console
SQS Test Messages Queue-triggered function processing Send messages via the SQS console
CloudWatch Logs Verify function executed correctly Check logs after sending test events

πŸ—οΈ Build A Testing Toolkit

Build a Testing Toolkit that demonstrates testing approaches:

  • Local Lambda testing with SAM CLI (invoke and start-api)
  • Unit tests using moto to mock DynamoDB and S3
  • Integration tests running against a deployed API Gateway stage
  • EventBridge rule testing with test events in the console
  • Sample event generation with sam local generate-event

Prerequisites

⚠️ Before You Start Read This

  • πŸ’‘ SAM local does NOT mock AWS. sam local invoke and sam local start-api run your code in a container but call real AWS services with your real credentials. So we create the DynamoDB table first.
  • πŸ’‘ API Gateway routes come from the template, not your code. A path only exists if it's declared in the template's Events. GET / will always say "Missing Authentication Token" because there's no root route.
  • πŸ’‘ Region must be consistent across your credentials, the table, and env.json. We pin us-east-1 everywhere so change it only if you use a different region, but change it everywhere.
  • πŸ’‘ Windows files need UTF-8 without BOM. A BOM causes Unable to unmarshal input: Expecting value: line 1 column 1. Create JSON files in VS Code and check the encoding in the bottom-right status bar.
  • πŸ’‘ After editing template.yaml, always sam build then restart start-api. The local server serves the built template in .aws-sam/build/, not your edited source.

Step 00: Set your region once for this session so every command agrees:

$env:AWS_REGION = "us-east-1"
aws configure set region us-east-1
Enter fullscreen mode Exit fullscreen mode

⚠️ If you already use a different region, substitute it consistently in every command and file below.

Step 01: Create the DynamoDB Table First

⚠️ Because SAM local calls real DynamoDB, the table must exist before you invoke anything.

aws dynamodb create-table `
  --table-name dev-orders `
  --attribute-definitions AttributeName=PK,AttributeType=S AttributeName=SK,AttributeType=S `
  --key-schema AttributeName=PK,KeyType=HASH AttributeName=SK,KeyType=RANGE `
  --billing-mode PAY_PER_REQUEST `
  --region us-east-1
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ The backtick ` is PowerShell's line-continuation character. On macOS/Linux/CloudShell, use \ instead.

⚠️ Wait until the table is ACTIVE:

aws dynamodb describe-table --table-name dev-orders --region us-east-1 --query "Table.TableStatus"
Enter fullscreen mode Exit fullscreen mode

⚠️ Repeat until it prints "ACTIVE" (usually a few seconds).

πŸ’‘ The table's key schema mirrors single-table design. PK (partition key) + SK (sort key) let one table hold many item types. This is the DynamoDB pattern the exam expects.

Step 02: Create the SAM Project

sam init --runtime python3.13 --name testing-toolkit --app-template hello-world
cd testing-toolkit
Enter fullscreen mode Exit fullscreen mode

πŸ’‘This scaffolds a project with hello_world/app.py, template.yaml, and tests/.

Step 03: Replace the Function Code

Open hello_world/app.py and replace its entire contents with this:

import json
import os
from datetime import datetime

import boto3

# πŸ’‘ Initialize the client OUTSIDE the handler so it's reused across warm
# invocations. Creating it inside the handler would rebuild it every call.
dynamodb = boto3.resource("dynamodb")
TABLE_NAME = os.environ.get("TABLE_NAME", "dev-orders")


def lambda_handler(event, context):
    """Order API: create an order (POST) or fetch one (GET)."""
    table = dynamodb.Table(TABLE_NAME)
    method = event.get("httpMethod", "GET")
    path = event.get("path", "/")

    try:
        # --- Create an order ---
        if method == "POST" and path == "/orders":
            body = json.loads(event.get("body") or "{}")
            order_id = f"ORD-{datetime.utcnow().strftime('%Y%m%d%H%M%S')}"

            table.put_item(Item={
                "PK": f"ORDER#{order_id}",
                "SK": "METADATA",
                "orderId": order_id,
                "customerId": body.get("customerId", "unknown"),
                "items": body.get("items", []),
                "status": "created",
                "createdAt": datetime.utcnow().isoformat(),
            })

            return _response(201, {"orderId": order_id, "status": "created"})

        # --- Get a single order ---
        if method == "GET" and path.startswith("/orders/"):
            order_id = event["pathParameters"]["orderId"]
            result = table.get_item(Key={"PK": f"ORDER#{order_id}", "SK": "METADATA"})
            item = result.get("Item")
            if not item:
                return _response(404, {"error": f"Order {order_id} not found"})
            return _response(200, item)

        return _response(200, {"message": "Testing Toolkit API",
                               "stage": os.environ.get("STAGE", "local")})

    except Exception as e:
        # πŸ’‘ Always handle exceptions. An uncaught error becomes a generic
        # 500 "Internal server error" at API Gateway, hiding the real cause.
        print(f"ERROR: {type(e).__name__}: {e}")
        return _response(500, {"error": type(e).__name__, "detail": str(e)})


def _response(status_code, body):
    return {
        "statusCode": status_code,
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps(body, default=str),
    }
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ The try/except matters for debugging. Without it, a failed DynamoDB call throws an uncaught exception and API Gateway returns a bare Internal server error. By catching it and returning the error name, you can see why it failed in the HTTP response itself.

Step 04: Fix the Template Routes

⚠️ The default template only maps GET /hello. Open template.yaml and replace the entire Resources: section's HelloWorldFunction so it matches your code's routes. Your HelloWorldFunction block should look exactly like this:

  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.13
      Timeout: 10
      Environment:
        Variables:
          TABLE_NAME: dev-orders
          STAGE: local
      Policies:
        - DynamoDBCrudPolicy:
            TableName: dev-orders
      Events:
        CreateOrder:
          Type: Api
          Properties:
            Path: /orders
            Method: post
        GetOrder:
          Type: Api
          Properties:
            Path: /orders/{orderId}
            Method: get
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ Routes are configuration, not code. API Gateway only serves paths declared under Events. Your function's internal if path == "/orders" logic never runs unless the template routes that path to it. This is why unknown paths return "Missing Authentication Token."

πŸ’‘ DynamoDBCrudPolicy is a SAM policy template. It grants least-privilege CRUD access to just the named table, instead of a broad AmazonDynamoDBFullAccess.

Step 05: Create the Environment Variable File

⚠️ SAM local reads env vars from a JSON file. Create a file named env.json in the project root (same folder as template.yaml) with this content:

{
  "HelloWorldFunction": {
    "TABLE_NAME": "dev-orders",
    "STAGE": "local",
    "AWS_REGION": "us-east-1"
  }
}
Enter fullscreen mode Exit fullscreen mode

⚠️ Windows Issue: Create this file in VS Code. Look at the bottom-right status bar. If it says "UTF-8 with BOM", click it β†’ Save with Encoding β†’ UTF-8. A BOM breaks JSON parsing with Expecting value: line 1 column 1 (char 0).

πŸ’‘ The top-level key is the function's logical ID. HelloWorldFunction matches the resource name in template.yaml. Pinning AWS_REGION here guarantees the function looks for the table in the same region you created it.

Step 06: Create a Test Event File

Create a file create_order.json inside the events/ folder :

{
  "httpMethod": "POST",
  "path": "/orders",
  "headers": { "Content-Type": "application/json" },
  "pathParameters": null,
  "body": "{\"customerId\": \"CUST-001\", \"items\": [{\"productId\": \"PROD-A\", \"quantity\": 2}]}"
}
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ This mimics the API Gateway proxy event. When API Gateway invokes Lambda, it wraps the HTTP request in this structure. body is a JSON string (escaped), not an object. That's why the code does json.loads(event["body"]).

⚠️ Save as UTF-8 without BOM.

Step 07: Build

sam build
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ Always build after editing template.yaml or your code. sam build copies everything into .aws-sam/build/, which is what sam local actually runs. Skipping this means you test stale code/routes.

⚠️ If sam build complains it can't find Python 3.13, run sam build --use-container (needs Docker running). It builds inside a Python 3.13 image so your local Python version doesn't matter.

Step 08: Test with sam local invoke (Direct Function Test)

This invokes the function once with your test event. No API Gateway involved.

sam local invoke HelloWorldFunction -e events/create_order.json --env-vars env.json
Enter fullscreen mode Exit fullscreen mode

Expected Result: a 201 response with an orderId, and no errors.

πŸ’‘ sam local invoke bypasses API Gateway. It calls the handler directly with the event you pass. Routing doesn't matter here, only the function logic and its (real) AWS calls. Because the dev-orders table exists in us-east-1, the put_item succeeds.

⚠️ If you see ResourceNotFoundException, the table isn't in the region the function is using.

Step 09: Verify the item actually landed in DynamoDB:

aws dynamodb scan --table-name dev-orders --region us-east-1 --query "Items"
Enter fullscreen mode Exit fullscreen mode

You should see the order you just created.

Step 10: Test with sam local start-api (Full HTTP Test)

Now test the real HTTP routing. Start the local API in one terminal:

sam local start-api --port 3000 --env-vars env.json
Enter fullscreen mode Exit fullscreen mode

Leave this running.

πŸ’‘ The function logs (including real errors) print in THIS terminal. If an HTTP call returns 500, look here for the actual stack trace.

Open a second PowerShell terminal and send requests.

Step 11: Create an order (POST)

⚠️ On Windows, use Invoke-RestMethod. PowerShell's curl is an alias for Invoke-WebRequest with different syntax, and inline curl.exe JSON escaping is painful.

Invoke-RestMethod -Uri http://localhost:3000/orders -Method Post -ContentType "application/json" -Body '{"customerId": "CUST-001", "items": [{"productId": "PROD-A", "quantity": 2}]}'
Enter fullscreen mode Exit fullscreen mode

macOS / Linux / CloudShell equivalents

curl -X POST http://localhost:3000/orders \
  -H "Content-Type: application/json" \
  -d '{"customerId": "CUST-001", "items": [{"productId": "PROD-A", "quantity": 2}]}'

curl http://localhost:3000/orders/ORD-20260702093000
Enter fullscreen mode Exit fullscreen mode

Expected Result: an object with orderId and status: created.

Step 12: Get that order (GET)

Copy the orderId from the previous response and:

Invoke-RestMethod -Uri http://localhost:3000/orders/ORD-20260702093000
Enter fullscreen mode Exit fullscreen mode

(Replace with your actual order ID.)

Expected Result: the full order item.

orderId    : ORD-20260702112404
status     : created
createdAt  : 2026-07-02T11:24:04.213247
PK         : ORDER#ORD-20260702112404
items      : {@{productId=PROD-A; quantity=2}}
customerId : CUST-001
SK         : METADATA
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ Expected "errors" that are actually correct:

  • GET http://localhost:3000/ β†’ "Missing Authentication Token:" there's no root route. Fine.
  • GET http://localhost:3000/orders (no ID) β†’ "Missing Authentication Token:" β€” POST /orders and GET /orders/{orderId} exist. Fine.

Stop the server with Ctrl+C when done. If port 3000 stays stuck:

Get-Process -Id (Get-NetTCPConnection -LocalPort 3000).OwningProcess | Stop-Process -Force
Enter fullscreen mode Exit fullscreen mode

Step 13: Unit Tests with moto (No AWS Needed)

πŸ’‘ It needs no real AWS, no Docker, no network. moto mocks AWS services in memory, so tests are fast and isolated.

pip install pytest moto boto3
Enter fullscreen mode Exit fullscreen mode

⚠️ Windows Path-Length If you installed Python from the Microsoft Store, its packages live under a very long base path (AppData\Local\Packages\PythonSoftwareFoundation...\LocalCache\...), and moto's deeply nested files push past Windows' 260-character limit, failing with OSError: [Errno 2] No such file or directory. Note that moto[dynamodb] does not help The extra only changes dependencies, not the files installed. Two fixes:

Fix A (permanent, recommended): Enable long paths. In an Administrator PowerShell, run New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force, then reboot and re-run the install.

Fix B (no admin): Use a virtual environment at a short path so the total path stays under the limit: python -m venv C:\venv then C:\venv\Scripts\Activate.ps1 then pip install pytest moto boto3. Don't put the venv inside the deep project folder. Use C:\venv.

Step 14: Create tests/unit/test_app.py:

import json
import os

import boto3
import pytest
from moto import mock_aws


@pytest.fixture
def dynamodb_table():
    """πŸ’‘ moto intercepts boto3 calls and simulates DynamoDB in memory.
    We create the table inside the mock BEFORE importing the handler."""
    with mock_aws():
        os.environ["AWS_DEFAULT_REGION"] = "us-east-1"
        os.environ["TABLE_NAME"] = "dev-orders"

        client = boto3.resource("dynamodb", region_name="us-east-1")
        client.create_table(
            TableName="dev-orders",
            KeySchema=[
                {"AttributeName": "PK", "KeyType": "HASH"},
                {"AttributeName": "SK", "KeyType": "RANGE"},
            ],
            AttributeDefinitions=[
                {"AttributeName": "PK", "AttributeType": "S"},
                {"AttributeName": "SK", "AttributeType": "S"},
            ],
            BillingMode="PAY_PER_REQUEST",
        )
        yield


def test_create_order_returns_201(dynamodb_table):
    # πŸ’‘ Import the handler AFTER the mock is active so its boto3
    # client is intercepted by moto.
    from hello_world.app import lambda_handler

    event = {
        "httpMethod": "POST",
        "path": "/orders",
        "body": json.dumps({
            "customerId": "CUST-001",
            "items": [{"productId": "PROD-A", "quantity": 2}],
        }),
    }

    result = lambda_handler(event, None)

    assert result["statusCode"] == 201
    body = json.loads(result["body"])
    assert body["status"] == "created"
    assert body["orderId"].startswith("ORD-")


def test_get_missing_order_returns_404(dynamodb_table):
    from hello_world.app import lambda_handler

    event = {
        "httpMethod": "GET",
        "path": "/orders/DOESNOTEXIST",
        "pathParameters": {"orderId": "DOESNOTEXIST"},
    }

    result = lambda_handler(event, None)
    assert result["statusCode"] == 404
Enter fullscreen mode Exit fullscreen mode

Step 15: Run the tests

python -m pytest tests/unit/ -v
Enter fullscreen mode Exit fullscreen mode

Expected Result: both tests pass with no real AWS calls.

πŸ’‘ Unit vs Integration: these moto tests are unit tests (fast, isolated, mocked). The Invoke-RestMethod calls against start-api in Step 9 hit real DynamoDB, closer to integration tests. The difference: mock for unit tests, real deployed resources for integration tests.

Step 16: Deploy and Run Integration Tests Against a Real Stage

πŸ’‘ Unit Tests (moto) prove your logic. Integration Tests prove the whole stack works (API Gateway, Lambda, and DynamoDB together) against a real deployed environment.

sam deploy --guided
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ sam deploy provisions real infrastructure via CloudFormation: the Lambda, the API Gateway stage, IAM roles, and the event mappings. Unlike sam local, this creates resources that cost money and must be cleaned up.

Step 17: When it finishes, copy the API Gateway endpoint URL from the Outputs. It looks like:

https://abc123.execute-api.us-east-1.amazonaws.com/Prod/

⚠️ Note the stage is Prod (SAM's default), so your full path is .../Prod/orders.

Step 18: Run integration tests against the deployed stage

Create tests/integration/test_api.py:

import os
import requests

# πŸ’‘ Pass the deployed URL via an environment variable so the same tests
# can run against dev, staging, or prod without code changes.
BASE_URL = os.environ["API_URL"].rstrip("/")


def test_create_and_get_order():
    # Create
    create = requests.post(f"{BASE_URL}/orders", json={
        "customerId": "CUST-TEST-001",
        "items": [{"productId": "PROD-A", "quantity": 1}],
    })
    assert create.status_code == 201
    order_id = create.json()["orderId"]

    # Retrieve the same order
    get = requests.get(f"{BASE_URL}/orders/{order_id}")
    assert get.status_code == 200
    assert get.json()["orderId"] == order_id


def test_missing_order_returns_404():
    resp = requests.get(f"{BASE_URL}/orders/DOESNOTEXIST")
    assert resp.status_code == 404
Enter fullscreen mode Exit fullscreen mode

Step 19: Install requests and run the tests, passing your real URL:

pip install requests
$env:API_URL = "https://abc123.execute-api.us-east-1.amazonaws.com/Prod"
python -m pytest tests/integration/ -v
Enter fullscreen mode Exit fullscreen mode

Expected Result: both tests pass against live AWS.

πŸ’‘ Testing pyramid in action. Many fast unit tests (moto), fewer integration tests (deployed stage), fewest end-to-end tests. Integration tests catch problems mocks can't: IAM permissions, API Gateway mapping, real DynamoDB behaviour.

Step 20: Test an Event-Driven Flow with EventBridge

Not everything is an API. Let's test an event-driven path using the EventBridge console.

Create a rule and target (console)

Step 20.1 Open the EventBridge console β†’ Rules β†’ Create rule

Step 20.2: Define rule detail

  • Name: order-placed-rule
  • Event bus: default

Click Next

Step 20.3: Build event pattern

  • Event source: Other
  • Creation method: Custom pattern (JSON editor).
  • Paste:
{
  "source": ["orders.api"],
  "detail-type": ["OrderPlaced"]
}
Enter fullscreen mode Exit fullscreen mode

Click Next

Step 20.4: Select target(s)

  • Target: AWS service β†’ Lambda function
  • Target location: Target in this current account
  • Function: testing-toolkit-HelloWorldFunction...

Click Next β†’ Next

Step 20.5: Click Create rule

Step 21: Send a test event

Step 21.1 EventBridge console β†’ Event buses β†’ default β†’ Send events

Step 21.2 Send events

  • Event source: orders.api
  • Detail type: OrderPlaced
  • **Event detail:
{ "orderId": "ORD-EVT-001", "customerId": "CUST-EVT", "items": [] }
Enter fullscreen mode Exit fullscreen mode

Click Send

Step 22: Verify it was delivered

Open the Lambda function β†’ Monitor β†’ View CloudWatch logs β†’ newest log stream. You'll see the event was received.

πŸ’‘ Event pattern matching is exact by default. source and detail-type must match precisely. Content-based filtering (prefix, numeric, exists) can match on detail fields. Sending test events from the console is the fastest way to validate rules.

⚠️ Our handler expects an API Gateway event shape, so it'll log the EventBridge event and likely return the default message. That's fine. The point here is proving the rule β†’ target delivery, not the handler's response.

Step 23: Generate Realistic Test Events with SAM

Hand-writing event JSON is error-prone. sam local generate-event produces the exact structure AWS services send.

# API Gateway POST event
sam local generate-event apigateway aws-proxy --method POST --path /orders --body '{\"customerId\":\"CUST-001\"}' > events/api_post.json

# S3 object-created event
sam local generate-event s3 put --bucket my-bucket --key orders/data.json > events/s3_put.json

# SQS message event
sam local generate-event sqs receive-message > events/sqs_message.json

# EventBridge (CloudWatch) scheduled event
sam local generate-event cloudwatch scheduled-event > events/scheduled.json
Enter fullscreen mode Exit fullscreen mode

List what's available:

sam local generate-event --help
sam local generate-event s3 --help
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ Generated events include all the metadata (request IDs, ARNs, timestamps) real triggers send, so your local tests behave like production. This is far more reliable than guessing the event shape by hand.


Troubleshooting

Symptom Cause Fix
Missing Authentication Token Path/method not in template Events Use a declared route; sam build + restart after edits
ResourceNotFoundException Table missing or wrong region Create table in the region from env.json & keep regions consistent
Internal server error (500) Uncaught exception in the function Read the start-api terminal for the real stack trace
Expecting value: line 1 column 1 JSON file saved with a BOM Save env.json/event files as UTF-8 without BOM
Binary validation failed for python Python 3.13 not on PATH sam build --use-container or install Python 3.13
curl fails in PowerShell curl is an alias for Invoke-WebRequest Use Invoke-RestMethod, or curl.exe with a body file
Port 3000 in use Old start-api still running Ctrl+C, or kill the PID on port 3000

⚠️ Clean Up Protocol

1 Delete the deployed stack

sam delete --stack-name testing-toolkit --region us-east-1
Enter fullscreen mode Exit fullscreen mode

2 Delete the DynamoDB table

aws dynamodb delete-table --table-name dev-orders --region us-east-1
Enter fullscreen mode Exit fullscreen mode

3 EventBridge β†’ delete order-placed-rule
4 CloudWatch β†’ delete the function's log groups
5 Remove the local testing-toolkit folder if you're done

πŸ’‘ sam delete tears down the whole CloudFormation stack, so you don't have to hunt for individual resources. The manually-created DynamoDB table isn't part of the stack, so delete it separately.**


πŸ—οΈ What You Built | πŸ“˜ Exam Concepts Recap

What You Built Exam Concept
Created the table before invoking SAM local calls real AWS, it does not mock services
Pinned region in env.json and every command Region consistency across credentials, resources, and config
Declared routes in the template Events API Gateway routing is configuration, not code
Used DynamoDBCrudPolicy SAM policy templates for least privilege
sam local invoke with an event file Direct function testing, bypassing API Gateway
sam local start-api + HTTP calls Local API Gateway emulation (integration-style)
Wrapped the handler in try/except Uncaught errors surface as generic 500s
moto @mock_aws unit tests Mocking AWS in memory for fast, isolated unit tests
Imported the handler after the mock moto must be active before boto3 clients are created
sam deploy --guided + integration tests Testing against real deployed resources
Passed the API URL via $env:API_URL Same tests run against any environment
EventBridge rule + test event Validating event-driven delivery and pattern matching
sam local generate-event Realistic event payloads for S3, SQS, API GW, etc.
sam delete for teardown CloudFormation stack removal
Recognised "Missing Authentication Token" Generic API Gateway "no matching route" message

Additional Resources


πŸ—οΈ

Top comments (0)