SnsTestReceiver Logo

SnsTestReceiver – Introduction

I would like to introduce a new helper tool I’ve developed for integration tests – SnsTestReceiver. This my new little pet helps to test SNS allowing the assertion of the notifications published in an easy and scalable way.

TL; DR;

SnsTestReceiver is a simple RESTful API that receives notifications from SNS and stores them in memory for quick access. The setup instructions, source code, and an example can be found on my GitHub here.

AWS SNS

As you’ve probably already know, AWS SNS provides an interface to publish notifications to one (forward) or more (fan out) subscriptions that could be using different protocols. SNS provides many more nice features – check the Getting started documentation on AWS.

App and SNS setup
A few popular AWS SNS subscription protocols

Integration tests challenge

There might be different definitions of an integration test, however I test application’s integration with the dependencies, and whenever possible using some realistic mocks. And if you are developing against AWS services locally or run integration tests in isolation, the Localstack is a must have tool :) I wrote about the Localstack setup for local development a while ago.

Each application, e.g. Lambda, has it’s own integration tests project. Inside the project there are multiple classes with the integration tests, and a common pattern is to invoke Lambda’s handler with an event, and assert that the expected action has been completed, e.g. SNS notification has been published. And you may have just a single Lambda in your solution, or even multiple Lambdas.

Code solution setup
Different setups using single SNS topic

Now to keep the testing simple, we could go with a few different options:

  • Unit test — i.e. don’t write any integration tests at all. An easy option which provides less confidence in the code option.
  • Use a dummy SQS queue — at first glance, this may look like the best option. You could stick to Localstack and just install the SQS SDK. While there are several SQS features that makes it highly scalable, however they are also quite inconvenient to use in the integration tests. Especially, if you have multiple integration test projects within the same solution that publish to the same SNS topic. See the diagram and the notes below.
Dummy SQS queue
Challenges with dummy SQS queues in the integration tests

SnsTestReceiver in a nutshell

If you’ve read so far, you must be intrigued about the solution I am proposing :) While it’s neither ground breaking nor super innovative, however it simplifies SNS testing, improves tests performances and scalability. See the revised diagram below.

SnsTestReceiver setup diagram
SnsTestReceiver simplifies SNS testing

Feature highlights:

  • Docker image is ready to use
  • RESTful API — get by message ID, or search for a matching string in the body
  • In memory storage for fast data access, and easy cleanup
  • SDK for .NET Core available as a NuGet package
  • There is no message wait time or visibility timeout
  • As long as you use unique IDs inside the message body, you can run all the integration tests in parallel

Setup SnsTestReceiver

The setup requires 3 simple steps.

1. Pull the docker image

Add the following into your docker-compose.yml file to pull the docker image, and expose port 5000:

sns-test-receiver:
  image: ignassakalauskas/sns-test-receiver:latest
  container_name: sns-test-receiver
  ports:
    - "5000:5000"

2. Setup SNS subscription

Assuming you have created arn:aws:sns:eu-west-1:000000000000:notifications SNS topic already, and have awslocal installed, simply create an HTTP subscription to the SnsTestReceiver’s /messages endpoint.

awslocal sns subscribe --topic-arn "arn:aws:sns:eu-west-1:000000000000:notifications" --protocol http --notification-endpoint http://sns-test-receiver:5000/messages

3. Assert the notification

In the assertion part of your integration tests, find the published notification by let’s say it’s ID.

// ... 
// When
await sns.PublishAsync(request);

// Then
var result = await testReceiver.SearchAsync(expectedId);
result.Should().HaveCount(1);
result.Single().Subject.Should().Be(request.Subject);
// ...

The full example code is available on GitHub.

Conclusions

While for a simple integration test it might suffice to use a dummy SQS queue and the SQS SDK, however once you start scaling, i.e. having more and more tests, and more and more assemblies that run in parallel, you need a more robust solution.

SnsTestReceiver addresses the dummy SQS queue shortcomings and provides an easy setup via docker image + SDK for .NET Core NuGet package.

The source code could be found on my GitHub.