.NET Workloads on AWS Lambda

MODULE 5

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

Many of .NET Lambda function project templates include an xUnit test project. Without a single change, you can execute the test in this project, and it will pass.
Create a new Lambda function using:
dotnet new lambda.EmptyFunction -n FunctionWithTestProject
Change to the FunctionWithTestProject/test/FunctionWithTestProject.Tests directory:
cd FunctionWithTestProject/test/FunctionWithTestProject.Tests
Open up that project in your IDE.

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
You will see output that looks like -
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)
You can, of course, open the test project in Visual Studio, VS Code, or Rider and execute the tests from there.

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.
In Rider, go to the Test menu, and select Run All Tests from Solution. This will run tests and open the Unit Test window where you will see the results.
For VS Code, you can easily run individual tests, or all the tests in a file
But if your tests are in multiple files, you will need a test runner extension. Please review the extension's documentation for more information or configuring and running 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

This tool allows you to invoke your Lambda functions locally with a payload you define and examine the response.

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

For the command line, VS Code, and Rider, you need to install the tool.

From the command line run:
dotnet tool install -g Amazon.Lambda.TestTool-6.0
If you already have it installed, update it using:
dotnet tool update -g Amazon.Lambda.TestTool-6.0

Command line testing

Using the example project above, from the command line navigate to the FunctionWithTestProject/src/FunctionWithTestProject directory.

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\"'
For bash use:
dotnet lambda-test-tool-6.0 --no-ui --payload '"hello"'
You should see output that looks like -
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 
In the response section you can see the output of the Lambda function.

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.

If you added log statements to your function, you would see them in the Log Output section.

Visual Studio

If you have the AWS Toolkit for Visual Studio installed, then you already have The AWS .NET Mock Lambda Test Tool.

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"
    }
  }
}
Start the application by pressing F5. The same page you saw above will open, enter your payload and press the Execute Function button.

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

To configure the AWS .NET Mock Lambda Test Tool for Rider, follow the instructions
here -

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
The project contains a Dockerfile. Open this, and you will see that Docker copies the files from the bin/Release/lambda-publish directory to the Docker container:
COPY "bin/Release/lambda-publish"  .
Open the Function.cs file and replace the Get(..) method with the following:
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;
}
Change to the TestingFunctionWithRIE/src/TestingFunctionWithRIE directory:
cd TestingFunctionWithRIE/src/TestingFunctionWithRIE 
Run the following:
dotnet build -c Release -o .\bin\Release\lambda-publish\ 
This will build the application, and place the binaries in the .\bin\Release\lambda-publish directory.

Make sure Docker is running, then build the container:
docker build -t testing_function_with_rie:latest .
When that completes, it's time to start the container:
docker run -it -p 9000:8080 testing_function_with_rie:latest TestingFunctionWithRIE::TestingFunctionWithRIE.Functions::Get
This command starts the docker container and exposes the internal port 8080 via port 9000 on your computer, and it passes the function handler as a parameter. The Runtime Interface Emulator uses this to execute the appropriate method in your application.

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"
    }
}
You will get a response like the following:
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
}
If you want to change the code and test again, use the following, it combines the .NET build, container build, and starts the container in one line:
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

The same principle applies when using Visual Studio, VS Code, and Rider.

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.
You will now be in your Lambda function code!


Using the AWS .NET Mock Lambda Test Tool

This also works from all three IDES, see above for instructions if you are using VS Code, or Rider.

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

In this module, you saw a variety of ways to test and debug your Lambda functions from both the command line and IDEs.

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

You’ve now completed Module 5, Unit Testing and Debugging. The following test will allow you to check what you’ve learned so far.

1. Which IDEs allow you to run Lambda function unit tests? (select one)

a. Visual Studio

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)

a. Mock .NET dependencies for your tests

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)

a. Managed runtime Lambda functions

b. Container based Lambda functions

c. Custom runtime Lambda functions

d. All types of Lambda function

Answers: 1-d, 2-d, 3-b

Conclusion

In this module, you saw a variety of ways to test and debug your Lambda functions from both the command line and IDEs.

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.

Was this page helpful?

HANDS-ON LAB: CREATE AND DEPLOY LAMBDA FUNCTIONS