- Integration Testing is important in serverless – that’s a fact
- Integration Testing is also problematic
- Integration Testing can be amazing with ServerlessSpy
- Integration Testing can be as simple as that
- Our System Under Test
- The Integration Test itself
- Integration Testing with complete visibility
- Integration Testing will be easier
Integration testing for serverless Event-Driven Architecture is as necessary as it is time-consuming. Boilerplate code to write, long waiting for tests completion, tedious debugging. But no more.
Enter ServerlessSpy, a library that eliminates all of the above and makes writing robust integration tests a pleasure. This guide explains how easy it is to use.
Integration Testing is important in serverless – that’s a fact
Much was said about testing serverless microservices. The bottom line is to test in the cloud and follow Testing Diamond / Honeycomb instead of a more traditional Testing Pyramid.
Testing Diamond and Testing Honeycomb put the most pressure on integration testing. This makes total sense for serverless, especially for Event-Driven Architectures with microservices asynchronously communicating through services like EventBridge or SQS. There are plenty of connection points, and automated integration tests are the best way to avoid a headache and tedious debugging if something breaks in the middle.
But how do we implement them?
Integration Testing is also problematic
We have a service.
It subscribes to EventBridge Event A
, performs Very Important Operations, and some time later sends Event B
back to the EventBridge.
How do we test it?
No worries, it’s doable.
We deploy an extra SQS queue that subscribes to Event B
on EventBridge.
Then, from our local test runner, we send Event A
and poll the SQS, waiting for the result…
We have a service. It’s invoked through API Gateway which triggers a cascade of events resulting in a Very Important Item added to the DynamoDB table. How do we test it?
Again, no worries. We make an HTTP request and then check every few seconds if the item exists in the DynamoDB table. Nice, we didn’t have to create any extra resources; we “only” had to write the code to poll the table periodically!
We have a service… Well, you see where it’s going. Integration testing often requires:
- deploying additional resources to subscribe to events you want to verify,
- writing extra testing boilerplate to poll for the results,
- waiting looong for tests to complete because periodic polling for results always adds latency.
I did all of the above – more than once. But there is a better way.
Integration Testing can be amazing with ServerlessSpy
The ServerlessSpy is a library developed by Marko from Serverless Life for crazy-fast and elegant integration tests.
The ServerlessSpy comes in three parts:
- A CDK Construct that, in 3 lines of code, creates necessary helper resources, instruments Lambda functions, and generates a list of events we can listen for
- A type-safe listener for the events with standard Jest matchers for validating event payloads
- A real-time events monitoring web console, which is a wonderful thing by itself
No boilerplate to write. No extra resources to create on your own.
Listener functions are generated based on the resources available in the CDK stack, so when I say type-safety, I mean no resource names passed as string arguments.
And, last but definitely not least, there is no resource polling. All events are delivered instantly through a WebSocket.
Integration Testing can be as simple as that
For the details on how ServerlessSpy works, it’s best to see the documentation. I’ll just say that using Lambda extension for instrumentation for this use case is really neat.
Our System Under Test
Here, let’s create a sample event-driven service and test it. It will communicate over EventBridge and contain a single Lambda function saving data to a DynamoDB table.
Few resources, nothing fancy. Also, the Processing Lambda source:
Now, let’s integrate ServerlessSpy.
Firstly, we add the ServerlessSpy
Construct to the stack:
It will create subscribers on our EventBridge and DynamoDB, instrument our Processing Lambda, and generate a list of available events to spy on.
In a real application, we should exclude the ServerlessSpy
from deployment on the production environment.
Secondly, we instruct the CDK to save CloudFormation Outputs to a file, as they will contain the WebSocket URL for the ServerlessSpy to connect to and the name of our EventBridge:
We should not commit the cdkOutputs.json
file to the repository and add it to .gitignore
.
Now, we deploy our stack:
After a moment, our service will be live and ready. Additionally, we should see a new file generated:
This class lists all events we can spy on. As you can see, we can even listen to Lambda console logs!
The Integration Test itself
What can we test? In a real project, I would just test whether we get a proper event back on the EventBridge. But for the purpose of this demo, let’s go nuts and also verify that the Lambda function is invoked and that it adds an item to the DynamoDB table.
It will look like this:
In beforeEach()
, we start the listener.
It will receive all events during our test and allow us to verify them.
In lines #27-36, we begin the test by sending EventA
to the EventBridge.
Then, we use ServerlessSpy to verify 3 events:
- in lines #38-40, we wait for the Processing Lambda to be invoked from the EventBridge Rule,
- in lines #42-51, we wait for the item to be put in the DynamoDB Data Table, and we verify it looks like expected,
- in lines #53-60, we wait for the
EventB
to be sent to the EventBridge and verify that it contains the expected message.
Note the condition
argument in all three wait
functions.
It filters out events that do not match the criteria.
Thanks to it, multiple tests can run in parallel and not interfere as long as we can distinguish events by some per-test unique property.
Finally, the order of the wait functions does not matter.
The ServerlessSpy receives all events from the moment we launch it in beforeEach()
, so there are no race conditions or lost events of any kind.
The whole test, with a warmed-up Lambda, takes 1.6 seconds 🚀
Integration Testing with complete visibility
Debugging failing integration tests can be annoying. We need to go through resources or logs, searching for the failing point… Well, no more.
ServerlessSpy comes with a real-time event monitoring web console. We just launch it:
and observe incoming events:
How awesome is that? And not only for integration tests but also for development in general.
Integration Testing will be easier
By now, you’ve probably guessed I kind of like the ServerlessSpy.
Yes. I think it’s elegant and awesome. And frankly, it deserves much more attention than it got so far. Aren’t people testing their services?! So maybe visit the repository and leave a star?
The ServerlessSpy is being actively developed. It currently supports Lambda, SQS, SNS, EventBridge, DynamoDB, and S3 events. Step Functions and Kinesis are on the roadmap!
You can find the complete source for this demo on GitHub: