RSpec Testing for Ruby AWS Lambda Functions

Recently, I wrote an AWS Lambda function at work in Ruby but I didn’t have a handy tool for creating a project skeleton like bundle gem does. That means nothing bootstrapped my testing for me. While copy+pasting code into pry proved that my simple function worked, that wasn’t quite good enough. What I really wanted was the ability to use RSpec with my Lambda code. After a cursory search of the Internet for some examples, I was left disappointed with how little I found. So, I rolled up my sleeves and figured it out.

First, let’s examine exactly how Ruby AWS Lambda functions work. You’ll probably find more complete guides out there, but I’ll try to go over enough to at least describe how it works from a Ruby perspective.

How Data Gets to Ruby

Ruby AWS Lambda functions require calling a method that takes two keyword arguments, one parameter called event and the other context. I tend to call this method lambda_handler(event:, context:). This is also the default name provided when you create a function via the Lambda Console UI.

When I’m using AWS API Gateway to invoke my functions, I use the Lambda proxy integration. This makes the event argument a Hash that contains information about the HTTP request. This hash contains top-level keys like queryStringParameters, headers, body, and httpMethod (plus many more), though most of these are optional, meaning you should verify they exist in your code before using them. The context argument always receives a Context object. I don’t tend to use this argument very often.

My Repository Layout

I tend to structure my repository such that my code is in src/function/. This usually includes a file called lambda_function.rb, my Gemfile, and the Gemfile.lock file (if I’m relying on external dependencies). I’m a fan of AWS SAM for deploying Lambda functions, so I put my [samconfig.toml])(https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html) and template.yaml in src/.

As is typical for RSpec, I put my tests in spec/, with a spec_helper.rb in there and a lambda_function_spec.rb file.

In the root of the repository, I put a Rakefile so I can run rake, a Gemfile (and its associated Gemfile.lock) for testing dependencies, a README.md, and since I use Jenkins, a Jenkinsfile. If you use a different CI product or if you have a more complicated function, you might have different files.

When everything is present, it ends up looking something like this:

An Example Function

With this context out of the way, we should be able to get to work on some actual code. Let’s start with a simple function. Here is a function that takes hexadecimal input and converts it to base64:

Hopefully that code isn’t too difficult to follow. It allows either a GET query string parameter (hexdata) or POST data and converts it to base64.

Writing Some Tests

Now to write some tests to verify that the function does what it is supposed to do. First, we’ll need to fill out our spec/spec_helper.rb file, which should look just like it always does:

The only difference in that file is the use of a require_relative where we pull in the function code.

Let’s confirm that the Gemfile looks right (this is the Gemfile right at the root of the repo):

And here’s what my Rakefile looks like (should be pretty typical):

Doesn’t hurt to make sure we have the .rspec file in the root of repository in good shape:

Now, finally, we can write some actual tests. Here’s the start of a lambda_funtion_spec.rb file:

Notice that I setup a context for POST requests, GET requests, and for some bad requests. In each scenario, all I do is call the lambda_handler() method, passing in the constructed event Hash for that scenario.

With a little luck, we should be able to run our tests and get the expected results:

Integrating into CI

Here’s an example Jenkinsfile stage for executing these tests:

Spread the love