AWS Chalice + Terraform Part 3: Testing Your App
This is the third part of the Chalice + Terraform series. You can check part 1 and 2 here:
AWS Chalice + Terraform: A serverless codebase that makes sense AWS Chalice + Terraform Part 2: Local development with LocalStackThis part is dedicated to testing our application, focusing on unit and integration testing. Let’s get started!
Writing unit tests
We will follow Chalice’s testing guide for the basics, then add additional tests on top of it.
First, let’s install pytest
, our test runner of choice:
1 | pip install pytest |
You can add these to requirements-ci.txt
for CI tooling purposes:
1 | -r requirements.txt |
Then, let’s create our test folders and the files we will need:
1 | mkdir -p tests/unit/ |
As you can see, we created a unit
subfolder within tests
, that way we can separate the integration tests that we will be writing later on.
The Chalice test client
Chalice has a built-in test client, located at chalice.test.Client
, capable of calling the lambda functions created within the project in multiple ways:
- Bare lambdas: https://aws.github.io/chalice/api#TestLambdaClient
- HTTP routes: https://aws.github.io/chalice/api#TestHTTPClient
- Events: https://aws.github.io/chalice/api#TestEventsClient
Testing REST functions
The following test checks the status code and payload of a REST enabled lambda:
1 |
|
1 | from chalice.test import Client |
Test a bare lambda function
In case you have a simple lambda without triggers, you can test it using client.lambda_.invoke
:
1 |
|
1 | def test_foo_function(): |
Note: If you run into an error like the following:
1 | FAILED tests/unit/test_app.py::test_index_function - TypeError: index() takes 0 positional arguments but 2 were given |
It could mean that you are trying to test a function triggered by @app.route
using client.lambda_.invoke
. This is reserved to functions declared using @app.lambda_function()
. Use client.http
instead.
Test SNS and SQS lambdas
Let’s test the SNS and SQS functions created on part 1 of the series, like so:
1 | def test_sns_handler(): |
Test a function under a certain stage
To use the configuration of a specific stage during tests, use:
1 | from chalice.test import Client |
See https://aws.github.io/chalice/topics/testing.html#environment-variables for more
See also
Check Chalice’s documentation on testing for more topics about testing, like mocking boto3
and using Pytest fixtures.
Writing integration tests with LocalStack
Now that we have unit tests in place, let’s go one step further: using LocalStack, we can trigger our functions like we would in a real environment, then check that the function actually ran. This way we can verify that our application is wired up correctly. If you need a refresher on how to set up your Chalice project to run against LocalStack, check Part 2 of the series.
Let’s start by creating a separate folder for our integration tests:
1 | mkdir tests/integration |
Testing strategy
AWS Lambda doesn’t provide a straight-forward way of checking that a function has run, so we need to automate the process you would normally do with the AWS Console: trigger your function, then going to CloudWatch to see if there are any execution logs. In this case, we will send a random UUID and then check that same payload was logged in our output, but feel free to adjust the assertion for your use case.
boto3 clients for LocalStack
We will be manipulating AWS resources created locally in LocalStack, so we need to tell boto3
that we are not hitting AWS servers but our own instead. We also need to mock AWS access keys:
1 | import boto3 |
We will be using the SNS and SQS clients to publish content to our topics and queues respectively, and the CloudWatch logs client to check the content of our log streams.
Triggering our Lambdas within tests
We are all set to write our integration tests:
1 | import json |
Note: These test expect that LocalStack is up and running, and that the Terraform infrastructure has been applied.
See also
- localstack-python-client for a
boto3
client pre-configured to run against LocalStack - See A journey to AWS Lambda integration testing with Python, Localstack, and Terraform by @melon.ruet for reference on how to init, apply and destroy Terraform resources using plain Python and the
subprocess
library. - python-terraform: A Python wrapper for the Terraform CLI
- If you want to test the infrastructure itself created by Terraform, check out Terratest.
What’s next
It’s time to get ready for production release, with tips for bigger codebases, logging, tracing, secrets management and more. To be published soon™