Module 5: Unit Testing and Debugging
LEARNING MODULE
Please note, you may follow along with the examples presented here, but you do not need to.
There are a variety of ways to test a Lambda function. The simplest is to use and build upon the unit tests provided in the AWS Lambda Project templates. A familiar test runner in an IDE, or the simple dotnet test is all you need to run the tests.
Another option is to use The AWS .NET Mock Lambda Test Tool. It is in preview at the moment. This lets you both test and debug your Lambda functions. The tool is included as part of the AWS Toolkit for Visual Studio. But you can download it for use with the command line, with VS Code, and Rider.
If you are developing Lambda functions that are deployed to containers, the Runtime Interface Emulator will let you test your functions running within the container.
There are third-party tools like localstack that you can use to test your functions locally, but the best way to perform functional tests is to deploy your Lambda functions to AWS and test there. AWS gives all accounts 1 million free requests per month; up to 3.2 million seconds of compute time per month. So there is very little reason not to test your functions on the AWS cloud.
See these two pages for more information on pricing - Get started with the AWS Free Tier, AWS Lambda pricing.
With these local testing tools, it is easy to perform some debugging too. A brief example will be shown later.
Time to Complete
30 minutes
xUnit Test Project
dotnet new lambda.EmptyFunction -n FunctionWithTestProject
cd FunctionWithTestProject/test/FunctionWithTestProject.Tests
Open the FunctionWithTestProject.Tests.csproj file, you will see a project reference to the Lambda function project.
<ProjectReference Include="..\..\src\FunctionWithTestProject\FunctionWithTestProject.csproj" />
From the command line, run the tests using:
dotnet test
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: < 1 ms - FunctionWithTestProject.Tests.dll (net6.0)
In Visual Studio, go to the Test menu, and select Run All Tests. This will also open the Test Explorer, where you will see the results of the tests.
When you change the code in the function, you update the test just as you would with a normal test project. Because this is a test project just like any other you write, you can use the same libraries to help you, such as moq.
The AWS .NET Mock Lambda Test Tool
As mentioned above, if you are using the AWS Toolkit in Visual Studio, the tool is already included.
You can use the tool with from Visual Studio, VS Code, Rider, or from the command line.
The command line allows you to run in two modes:
1. With a web browser based UI
2. With no UI
The next few steps show how to use the tool from the command line, but feel free to skip ahead to the section on Visual Studio if that is your preference.
Installing the tool
From the command line run:
dotnet tool install -g Amazon.Lambda.TestTool-6.0
dotnet tool update -g Amazon.Lambda.TestTool-6.0
Command line testing
To run the test from the command line, you have to specify the --no-ui option, and pass in a payload. The escaping of the payload will vary depending on the shell you are using. The below works for PowerShell.
From the command line, run:
dotnet lambda-test-tool-6.0 --no-ui --payload '\"hello\"'
dotnet lambda-test-tool-6.0 --no-ui --payload '"hello"'
AWS .NET Core 6.0 Mock Lambda Test Tool (0.12.3)
Loaded local Lambda runtime from project output C:\dev\FunctionWithTestProject\src\FunctionWithTestProject\bin/Debug/net6.0
Executing Lambda function without web interface
Found Lambda config file C:\dev\FunctionWithTestProject\src\FunctionWithTestProject\aws-lambda-tools-defaults.json
... Using config file C:\dev\FunctionWithTestProject\src\FunctionWithTestProject\aws-lambda-tools-defaults.json
... Using function handler FunctionWithTestProject::FunctionWithTestProject.Function::FunctionHandler
... Using payload with the value "hello"
... Setting AWS_PROFILE environment variable to default.
... No default AWS region configured. The --region switch can be used to configure an AWS Region.
Captured Log information:
Request executed successfully
Response:
"HELLO"
Press any key to exit
Web UI testing, launched from command line
You can also use the tool from the command line to launch a Web UI for testing.
In the UI, you can enter your own payload, but you won't need to worry about escaping in this case. Or you can select from a set of example request payloads. These allow you to simulate the requests from other AWS services, such as S3, Kinesis, SQS, etc.
But for now, enter "hello" (including the quotes) as the payload, and press the Execute Function button.
In the response section, you will see what the function returned.
Visual Studio
Open the FunctionWithTestProject/src/FunctionWithTestProject project in Visual Studio.
Visual Studio will create a Properties directory, and inside that directory a launchsettings.json file which wires up The AWS .NET Mock Lambda Test Tool to your code.
The file will look something like this -
{
"profiles": {
"Mock Lambda Test Tool": {
"commandName": "Executable",
"commandLineArgs": "--port 5050",
"workingDirectory": ".\\bin\\$(Configuration)\\net6.0",
"executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe"
}
}
}
VS Code
To use the AWS .NET Mock Lambda Test Tool in VS Code, follow the instructions here - https://github.com/aws/aws-lambda-dotnet/tree/master/Tools/LambdaTestTool#configure-for-visual-studio-code
Note:
1. you should change dotnet-lambda-test-tool-3.1 to dotnet-lambda-test-tool-6.0.
2. if you are using Linux/Mac dotnet-lambda-test-tool-6.0 will not have a .exe extension.
Rider
Containers and Runtime Interface Emulator
If you are deploying Lambda functions inside containers, you can perform another layer of local testing using the AWS Runtime Interface Emulator (RIE). You don't need to install any new tooling, the tooling will automatically be packaged up inside the container you build.
For this example, you are going to write a serverless application that will have two function handlers.
Create a new container based project using:
dotnet new serverless.image.EmptyServerless --name TestingFunctionWithRIE
COPY "bin/Release/lambda-publish" .
public APIGatewayProxyResponse Get(APIGatewayProxyRequest request, ILambdaContext context)
{
context.Logger.LogInformation($"Get method invoked. You requested {request.PathParameters["Id"]}");
var response = new APIGatewayProxyResponse
{
StatusCode = (int)HttpStatusCode.OK,
Body = $"You were looking for something with an Id of : {request.PathParameters["Id"]}",
Headers = new Dictionary<string, string> {
{
"Content-Type",
"application/json"
}
}
};
return response;
}
cd TestingFunctionWithRIE/src/TestingFunctionWithRIE
dotnet build -c Release -o .\bin\Release\lambda-publish\
Make sure Docker is running, then build the container:
docker build -t testing_function_with_rie:latest .
docker run -it -p 9000:8080 testing_function_with_rie:latest TestingFunctionWithRIE::TestingFunctionWithRIE.Functions::Get
If you have more than one function handler in your code, you would test each in turn, starting the container and passing the appropriate function handler each time.
Finally, make a HTTP request to the Runtime Interface Emulator. You can use Fiddler, Postman, Insomnia, etc. Below is the request to use with VS Code Rest Client, or Rider Http Client:
POST http://localhost:9000/2015-03-31/functions/function/invocations HTTP/1.1
content-type: application/json
{
"PathParameters": {
"Id": "999"
}
}
HTTP/1.1 200 OK
Date: Fri, 29 Jul 2022 18:03:56 GMT
Content-Length: 148
Content-Type: text/plain; charset=utf-8
Connection: close
{
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
},
"body": "You were looking for something with an Id of : 999",
"isBase64Encoded": false
}
dotnet build -c Release -o .\bin\Release\lambda-publish\ ; docker build -t testing_function_with_rie:latest . ; docker run -it -p 9000:8080 testing_function_with_rie:latest TestingFunctionWithRIE::TestingFunctionWithRIE.Functions::Get
Testing your Lambda function on AWS
The above tools are great for testing your Lambda function locally, and even though there are tools to help you with simulated AWS environments, the best way to test your function is in the AWS Lambda service.
AWS has a generous, always free tier, allowing you to execute a million Lambda requests a month for free, and if you go above that limit, the next 1 million executions will cost $0.20, or $0.0000002 per execution. There are a few caveats around pricing to do with memory usage and time running, see - Get started with the AWS Free Tier and AWS Lambda pricing for more information.
While the emulator tools might be great if you don't have internet connectivity, you may find they don't behave exactly the same as the real AWS services. Testing your Lambda functions in AWS means that there will be no surprises when the time comes to deploy your code.
All you need to do is create a .NET test project, the xUnit template is a good choice. From there, write functional tests targeting the Lambda functions you deploy to AWS.
dotnet new xunit -n LambdaTestProject
For a walk-through of testing .NET Lambda functions deployed to AWS, see the blog post Developing .NET Core AWS Lambda functions.
Debugging
You may want to debug your Lambda function locally from time to time. There are two easy ways for you to do this:
1. Putting a breakpoint in the unit test project, then stepping into the function.
2. Starting The AWS .NET Mock Lambda Test Tool from within your IDE, putting a breakpoint in your function, and executing the function from the test tool.
Using the unit test project
Open the test project and put a breakpoint in the code right where it calls the Lambda function.
Debug the test. When you hit the breakpoint, step into the Lambda function.
Using the AWS .NET Mock Lambda Test Tool
In Visual Studio, open the function project, not the test project.
Put a breakpoint in the function handler. In the Web UI, add your function input, and press execute.
Your breakpoint will be hit, and you can debug your Lambda function just like any other method.
Conclusion
As you get used to writing Lambda functions, you may move between the different ways of testing your functions, but you are strongly encouraged to use the real AWS Lambda service as soon as possible to test your code. It is the best place to do it.
You also saw how to debug a function locally, allowing you to step through the code and see what is happening.
Knowledge Check
1. Which IDEs allow you to run Lambda function unit tests? (select one)
b. VS Code
c. Rider
d. All of the above
2. What does the AWS .NET Mock Lambda Test Tool let you do? (select one)
b. Mock AWS services for your Lambda function to call
c. Run performance tests on your Lambda functions
d. Invoke your function code locally
3. What type of Lambda function is the Runtime Interface Emulator suitable for? (select one)
b. Container based Lambda functions
c. Custom runtime Lambda functions
d. All types of Lambda function
Answers: 1-d, 2-d, 3-b
Conclusion
As you get used to writing Lambda functions, you may move between the different ways of testing your functions, but you are strongly encouraged to use the real AWS Lambda service as soon as possible to test your code. It is the best place to do it.
You also saw how to debug a function locally, allowing you to step through the code and see what is happening.