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.
72
91%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
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:
Amazon.Lambda.TestUtilities for library-level fakes plus the
Mock Lambda Test Tool
UI for manual invocation.
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 xunitPer Amazon.Lambda.TestUtilities:
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());
}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);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);Per aws-extensions-for-dotnet-cli:
dotnet tool install -g Amazon.Lambda.Tools
dotnet lambda invoke-function MyFunction --payload '"hello"'For local-only testing, prefer handler-direct invocation.
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-pattern | Why it fails | Fix |
|---|---|---|
Spawn dotnet lambda invoke per test | Network calls; slow; rate-limited | Handler-direct invocation |
Skip TestLambdaContext | Null context fails at runtime | Always pass one |
RemainingTime = TimeSpan.MaxValue | Timeout logic never exercised | Realistic remaining time |
Hand-rolled APIGatewayProxyRequest | Schema drift | sam local generate-event fixture |
Missing [assembly: LambdaSerializer] | Runtime deserialization fails | Register the serializer in tests too |
| Mocking the Logger | Loses TestLambdaLogger.Buffer replay | Use the default TestLambdaContext logger |
next/response polling) is not exercised.