Github Actions & End-to-End Testing with Testcontainers & LocalStack

Search for a command to run...

No comments yet. Be the first to comment.
Bite-size LocalStack features inspired by frequently asked questions. Each post zooms in on a single example-backed topic, unlocking insights to elevate user knowledge and pave the path to success.
Using Docker for local development offers several advantages: isolation, consistency, reproducibility, efficiency, portability, easy cleanup, and security. You get the gist. These are also the great advantages of LocalStack shipping as a Docker image...
Learn how to test the EventBridge archive and replay feature with a Lambda target using LocalStack and AWS CLI — all on your local machine.

Learn how to deploy and debug ECS tasks locally with LocalStack by attaching a remote debugger in VS Code to facilitate local development & testing!

Learn how to run an EC2 instance using LocalStack's core cloud emulator and AWS CLI to facilitate development & testing right on your local machine!

Introduction In today’s tech landscape, large language models (LLMs) and AI are transforming both tech-centric and traditional businesses. AI functionality is being integrated across various platforms to enhance user experience. However, developing a...

After seeing how LocalStack Cloud Pods help teams work better together, let's look at the other ways companies can keep their snapshots in their own court. For environments with stricter security policies, there are a few storage options that help pr...

If you’re a developer dealing with multiple systems, you already know there’s no way around end-to-end testing. You need to make sure that all your pieces fit together constantly. And if you’re a business releasing new software features often, you need a CI/CD pipeline that builds, tests, and releases software the moment you push your code. A comprehensive automated test suite is not as straightforward and easy as unit testing but it is essential. So, how can we bring the simplicity and speed of unit tests into these integration tests? On top of that, we’d prefer live services over mocked behavior for testing, aiming to replicate production behavior during tests.
This is where Testcontainers and LocalStack work beautifully together to bring you the best of integration tests and cloud services on your machine and in your CI/CD pipeline.
Today, we’re discussing the ease of setting up a workflow that will always make sure our system behaves as expected. Even visually, in a diagram, beyond the bigger picture, we can see that two key areas go hand in hand: setting up the right infrastructure and holding that in check using tests.

You can find the full working example in the Stack Bytes repository, the workflow, as is standard, will be under .github/workflow, and the test examples sit in the github-actions-testcontainers folder.
When you make changes to your application's code and push it to GitHub, GitHub Actions automatically kicks in. It takes your code and starts testing it against different scenarios, just like trying different puzzle pieces. This is especially useful for end-to-end testing, where you want to see if the entire application works well together. In this case we’re interested in testing the Lambda functions, but there are so many other apps that we can plug in.
With Testcontainers, you will set up a "sandbox" environment to put the puzzle pieces together, in our case, the AWS services we need. GitHub Actions runs your tests, simulating real user interactions. It's like a rehearsal before the big show – making sure everything runs smoothly before it's in front of your users. Another great aspect of using Testcontainrs with LocalStack is that all the steps leading up to the tests are already taken care of: Testcontainers manages the lifecycle of LocalStack, while provisioning the infrastructure is the same as in production, weather it’s initialisation hooks, Terraform, CDK, or CLI.
Here are some of the things that will make your life easier when you’re using Testcontainers in CI or on your machine:
Use a waiter to make sure your Lambdas are ACTIVE and not just created:
LambdaWaiter waiter = lambdaClient.waiter();
GetFunctionRequest getFunctionRequest = GetFunctionRequest.builder()
.functionName("create-quote")
.build();
WaiterResponse<GetFunctionResponse> waiterResponse = waiter.waitUntilFunctionActiveV2(
getFunctionRequest);
waiterResponse.matched().response().ifPresent(response -> LOGGER.info(response.toString()));
Use this nifty configuration to scan the LocalStack logs and make sure your instance is in the right state before the tests are allowed to start:
protected static LocalStackContainer localStack =
new LocalStackContainer(DockerImageName.parse("localstack/localstack-pro:2.2.0"))
........
.waitingFor(Wait.forLogMessage(".*Finished creating resources.*\\n", 1));
It’s been recently discovered that some Lambda containers are not removed when the tests end. Don’t worry, a fix is on the way, but in the meantime, you can use a dedicated function to clean up at the end of the test suite (only use this on your machine):
protected static void cleanLambdaContainers() {
try {
String scriptPath = "src/test/resources/delete_lambda_containers.sh";
ProcessBuilder processBuilder = new ProcessBuilder(scriptPath);
processBuilder.inheritIO();
Process process = processBuilder.start();
int exitCode = process.waitFor();
System.out.println("Script exited with code: " + exitCode);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
#!/bin/bash
# get a list of running container ids with the word "lambda" in their names
container_ids=$(docker ps -q --filter name=lambda)
# loop through the ids and stop and remove each container
for id in $container_ids; do
echo "Stopping and removing container: $id"
docker stop $id
docker rm $id
done