Serverless architecture (eg. Hexagonal) was one of the hottest topics in 2018. Just take a look at Medium articles and the number of threads and discussions on Reddit, Twitter, and other social media. It’s becoming more and more popular - and some well-known companies have started to use serverless architecture in their projects (Netflix or Coca-Cola, for example). However, popularity also serves to reveal some drawbacks and challenges of introducing serverless architecture. One of the biggest challenges is testing which is much different than in “traditional” applications. In this article, I would like to share some hints and tips about testing a serverless application >>.
The obvious difference between a traditional and serverless application is that it requires a lot of collaboration with external services - in the AWS world, it can be API Gateway, Dynamodb or S3, for instance. It’s often hard or even impossible to integrate with real services. Fortunately, there are some useful tools that can help with emulating some external services to help with writing tests. Let’s talk a little about possible tests that we can perform in the serverless world.
Local serverless testing
Firstly, to speed up every local development developers should be able to invoke code locally. At Solidstudio Software House we choose NodeJS to write our lambda functions because it’s lightweight and it’s easy to execute your code locally. We also find the serverless framework very handy as it that allows us to execute NodeJS code with some input parameters that can be really helpful.
Let’s imagine that our lambda subscribes to AWS S3 event for further processing. It thus has to consume the S3 event. It could be very helpful if we could use that event and invoke lambda as if it was consuming a real S3 event. We can easily capture the real AWS event and save it in a local JSON file. We can later use it for input of our lambda without actually using our infrastructure.
Unit testing in serverless application
Of course, invoking lambda locally for several scenarios every time we want to change a single line of code isn’t very efficient. That’s why we need to write some automated tests. The most basic and easiest type of tests is, of course, unit testing. It's a must-have in all kinds of projects and the serverless environment is no different. The good thing is that serverless functions are "well-modularize” and so can be unit tested easily.
Like in any other system, we need to have decent code coverage in unit testing. We need to simply mock every interaction with external services. If we want to simulate some AWS events, we can use the captured AWS events discussed earlier. Every modern programming language has a framework to write and mock unit tests with. For NodeJS, we can use Mocha or Jasmine which offers a very easy way to simulate external events. Building and running tests must be a part of building a deployable package.
Going into the real world
Once you have your code well-tested locally and are confident that the code is working correctly, you should deploy it to the development environment. This process need to be automated to avoid the potential risk of manual deployment. In serverless development, it’s crucial to test your function on the real environment because of some configurations like IAM permissions or AWS lambda limitations that don’t occur in the local environment like memory, disk space, timeout, payload size. Sometimes we forget about these limitations and something that works well on our local machine may fail in the real AWS world.
When our code has successfully reached the dev environment, you can check its behaviour by emitting event that particular functions are subscribed to. You can, for instance, upload a file to the target bucket and check if particular lambda behaves as we expect. Obviously, it's just a technique to debug and improve the development process - and testing cannot rely on it. We need some automated tests to make sure that all our functions work well after every change. We should run the entire test suite after applying changes to a single function.
Integration testing in serverless application
In a serverless project, we also need to have a decent integration of the test suite to make sure our application works as expected. Each lambda usually handles one event and most of the time we need several lambdas to cover our business story. Creating a user by the REST API can trigger events that will be consumed by several lambdas that will handle adding a user to DynamoDB, uploading photos to S3, or sending greeting emails. So, one integration test must make sure that all these three lambdas are invoked and done their job properly.
With a serverless framework along with Event Gateway, we can very simple hook up our functions to particular events and trigger our functions. Then we need to have code that will verify whether the lambdas have done their job well. It's good to write tests in one language rather than mix several techniques. The programming language should work well with AWS and update their SDK quite often so it works with the newest version of services.
Each test suite has to start with an emitting event and wait till lambdas react on it. That could be challenging as there’s no easy way to indicate whether a lambda is complete. One approach could be invoking some lambda and waiting for some fixed calculated time before testing - for instance, we could take lambda timeout as our wait time. However, it's not a good idea since delays may happen or some lambdas will finish before that time - its either risky or inefficient.
The best approach is checking periodically if some condition is fulfilled - if we expect an object to appear on S3 then we can check whether it’s there, as well as check for data in our storage (DynamoDB, RDS etc). We can then finish the test as soon as the lambda has finished its job, or fail if the outcome is not what we expected or the lambda hasn’t finished its work in a reasonable amount of time.
Serverless testing in practise
At Solidstudio Software House, we’ve started our latest project in a fully serverless architecture. We noticed that it requires a significant mindset change, but developers who have in microservice architecture should be able to move to serverless quite smoothly. As serverless architecture becomes more and more popular, they can take advantage of reliable and easy-to-use tools. We find serverless framework and event gateway as the best technology choice.
P.S. If you’d like to stay in touch, please sign up for our newsletter: no spamming, just information about the latest content.