profile

Master Serverless

How to apply the TDD mindset to serverless

Published about 1 month ago • 3 min read

How to apply the TDD mindset to serverless

Read on my blog

Read time: 3 minutes.

Testing is an integral part of software development. Your tests are a living documentation of your system. They inform others how to use your system, but they are so much more than that.

One of the most understood parts of Test-Driven Development (TDD) is the "Driven" part of the name.

It's not just about "writing tests before you write the code". If your tests do not inform and drive your API design, then you're not really doing TDD.

When I say "API", I mean the general meaning of the term, not the API spec for HTTP APIs, although they are part of it. In the context of a serverless application, you have to cover multiple APIs with your tests.

  • For an HTTP API, there are the API contracts you agree with the caller.
  • For an event-driven architecture, there are event contracts that everyone agrees on.
  • For the Lambda function, there is the handler function's signature (its input and output types). In most cases, we don't have control over these, as the function's event source dictates them.
  • The code modules that are executed when a Lambda function is invoked.

Different types of tests [1] can drive the design of the different APIs above.

For example, end-to-end tests exercise the system from the outside. An e2e test will exercise an HTTP API by calling its HTTP endpoints as a client would. They should tell you if your API is difficult to use and drive you towards a better API design.

Similarly, unit tests exercise the business logic encapsulated in the code modules. They should tell you if you have the right abstraction and modularity in place and drive you towards a better code organization.

To apply the TDD mindset to serverless development:

  1. Write e2e tests for your API before you even think about the Lambda functions behind those API endpoints.
  2. Use the tests to identify problems with the API design. Is data missing from the API responses? Are we asking the caller to make multiple calls when we can do everything they want in one?
  3. Iterate and improve.
  4. Once the API spec is set, you can map API endpoints to Lambda function(s).
  5. Now, it's time to implement the Lambda function handlers.
  6. Use unit tests to test your domain logic and use them to drive your API design.

Does this look similar to what you'd do if you were building serverful applications running on containers or EC2?

It should! The environment our code runs in should not influence how we can use tests to drive our design.

But with serverless, we want to leverage the cloud to its full potential and delegate the heavy lifting to the cloud provider. It changes what and how much code we have to write and maintain. So, it changes how and what we need to test.

For example, I'd delegate authentication and authorization to API Gateway. So, there is no authentication-related code in my Lambda function, and there's no need for me to write unit tests for it. Instead, authentication is checked as part of my e2e tests. I might even have an e2e test to make sure that unauthorized requests are rejected by API Gateway.

Similarly, most of my Lambda functions are simple and do not have complex business logic. So, there is a low return on investment (ROI) from unit tests. Instead, I focus on testing the integration with the external dependencies (such as DynamoDB tables) with "remocal tests".

I wrote about my testing strategy for serverless applications [2] previously. If you want to learn more about testing serverless applications, please read that.

But remember, tests are not just for catching bugs and preventing regressions. They are also living documentation for your system and a way to drive its design.

Links

[1] What is the Test Honeycomb? (and why you should care)

[2] My testing strategy for serverless applications

Whenever you're ready, here are 3 ways I can help you:

  1. Production-Ready Serverless: Join 20+ AWS Heroes & Community Builders and 1000+ other students in levelling up your serverless game. You can also get 15% OFF as a newsletter subscriber with the code LEVELUP15.
  2. Consulting: If you want to improve feature velocity, reduce costs, and make your systems more scalable, secure, and resilient, then let's work together and make it happen.
  3. Join my FREE Community on Skool, where you can ask for help, share your success stories and hang out with me and other like-minded people without all the negativity from social media.

Master Serverless

by Yan Cui, AWS Serverless Hero

Join 8k+ readers and level up you AWS game with just 5 mins a week. Every Monday, I share practical tips, tutorials and best practices for building serverless architectures on AWS.

Read more from Master Serverless

"The offer is strong with this one." Hey there! I've got an awesome offer for you this Star Wars Day. I've partnered with the best AWS content creators to give you 30% off on a fantastic range of AWS books and courses! From left to right: me, Philip Riecks, Sandro Volpicella, Alex DeBrie, Daniel Galati and Tobias Schmidt. Enter the code TBMAPRBD at checkout to get your discount. But hurry, this offer ends in 3 days. Check out these deals: 30% OFF on AppSync Masterclass: Learn fullstack...

6 days ago • 1 min read

I can’t believe it’s May already! It’s been a busy few months here. Here’s what I've been up to and what you might have missed. Blog posts How to handle execution timeouts in AWS Step Functions How to apply the TDD mindset to serverless Here are four ways you can implement WebSockets using serverless DynamoDB now supports cross-account access. But is that a good idea? When to use Step Functions vs. doing it all in a Lambda function When to use API Gateway vs. Lambda Function URLs First...

9 days ago • 2 min read

Step Functions lets you set a timeout on Task states and the whole execution. By default, a Task state times out after 60 seconds. But an execution can run for a year if no TimeoutSeconds is configured. To a user, the execution would appear as “stuck”. AWS best practices recommend using timeouts to avoid such scenarios [1]. So it’s important to consider what happens when you experience a timeout You can use the Catch clause to handle the States.Timeout error when a Task state times out. You...

18 days ago • 2 min read
Share this post