Motivation
- Add integration tests without fakes for AWS services.
- Run tests locally and in a build machine without AWS accounts.
Application architecture
We have a typical serverless solution - lambda that is triggered by AWS resource and store result in the S3 bucket. It ia two popular AWS serverless services.
Integration tests solution
You need to make sure that Lambda put documents into an S3 bucket. This implementation consists of the next concepts:
- xUnit unit tests tool
- lambda with configured dependecy injection
- s3 bucket that runs in docker using LocalStack
Run container
To launch a container in code we will use Testcontainers. Testcontainers is a library that is built on top of the .NET Docker remote API and provides a lightweight implementation to support your test environment in all circumstances. This library supports pre-defined packages for containers or you can use your .dockerfile
. We will use a pre-defined package for LocalStak. LocalStack is a cloud service emulator that runs in a single container for AWS service. LocalStack supports a growing number of AWS services.
var container = new LocalStackBuilder()
.Build();
Configure xUnit with container
You need to start the container before the tests run and stop after the tests run. To do that we will use two features IAsyncLifetime and IClassFixture. And next, add access to the container(local URLs) in tests.
public class LocalStackFixture : IAsyncLifetime
{
public readonly LocalStackContainer Container =
new LocalStackBuilder().Build();
public Task DisposeAsync()
=> container.StopAsync();
public Task InitializeAsync()
=> container.StartAsync();
}
public class LambdaHandlerTest : IClassFixture<LocalStackFixture>
{
private readonly LocalStackFixture fixture;
public LambdaHandlerTest(LocalStackFixture localStackFixture)
=> fixture = localStackFixture;
}
Initialize AWS service
You need to have a pre-defined S3 bucket, before putting documents. LocalStck has built-in AWS CLI(awslocal
). The awslocal
command has the same usage as the aws
command. For detailed usage, please refer to the man pages of aws help, but of course, it has some limitations 😉. That means, we just need to run create bucket command and start running the test when a command is completed.
public LocalStackContainer Container = new LocalStackBuilder()
.WithStartupCallback((container, ct) =>
container.ExecAsync(new[]
{
"awslocal", "s3api", "create-bucket", "--bucket", BUCKETNAME
}, ct))
.Build();
Lambda Tests
The final test with all configurations will look like that
[Fact]
public async Task HandleShoudPutDocToS3Test()
{
var client = new AmazonS3Client(new AmazonS3Config
{
ServiceURL = fixture.Container.GetConnectionString()
});
var sut = new LambdaHandler(collection => collection
.AddSingleton<IAmazonS3>(client)
.PostConfigure<Config>(c => c.BucketName =
LocalStackFixture.BUCKETNAME));
var docId = Guid.NewGuid().ToString();
await sut.Handle(new TestLambdaContext
{
AwsRequestId = docId
});
var res = await client.GetObjectAsync(
LocalStackFixture.BUCKETNAME,
$"{docId}.txt");
_ = res.HttpStatusCode.Should().Be(HttpStatusCode.OK);
}
Conclusions
- To run tests locally or in the build machine we only need to install docker and run standard tests command
dotnet test
. - Testcontainers - support a loot of pre-defined packages that you can use the integration tests.
- LocalStack makes development easier and cheaper when work with AWS services.
And finally, the source code in the GitHub repository
Serverless integration tests
A public feed with available products that updates every day
Business problem
- Integration tests for serverless solution
Requirements
- Docker
Implementation
- S3 public buckets with available documents
- Lambda updates document
Top comments (0)