AWS API client mocking with Go

Brandon Strohmeyer
3 min readJul 9, 2021

While learning how to mock the AWS API client for unit testing in Go, I discovered the majority of resources are targeted at the Go veteran. The aim of this article is to bridge the gaps left and explain in practical terms how to get started.

The Setup

For the purposes of this blog, we have a single task: return a list of CIDR blocks associated with an AWS VPC. A simple pattern would be:

main() calls ListVpcCidrBlocks(). A new AWS session is created, and then used to create the concrete EC2 client, which makes the DescribeVpcs call. This is great, except it makes unit testing difficult as the only way to test it is against a real live VPC running in AWS.

The Dependency Injection

Dependency Injection is a topic all its own, but for our purposes it can be defined as a method to make code more modular, which in turn makes it easier to test. Instead of bundling the creation of the EC2 client inside of ListVpcCidrBlocks() we are now creating the client outside of the function and injecting it as an input parameter:

Functionally, nothing has changed. Both of the above examples return the exact same result.

The Service Client Interface

AWS has an official blog talking about using API interfaces for testing, but the gist is that instead of using a concrete service client (What you get from a call to ec2.New(sess)) you can instead use the service interface:

We’re now importing the github.com/aws/aws-sdk-go/service/ec2/ec2iface package and the ListVpcCidrBlocks function signature has changed from taking a pointer to the EC2 client (*ec2.EC2) to taking the EC2API interface. Functionally nothing has changed from the first example, we’re still returning a list of CIDR blocks attached to the VPC, but now from a testing perspective we have a way in.

The Test

Because we now are using the service interface instead of a service client, we are able to “intercept” function calls and return our own data instead of making the API call to AWS. A look at the full test to give context:

First, the mocked EC2 interface is created along with the methods that we want to “intercept” as fields in the mockedEC2 struct:

ec2iface.EC2API here is an embedded anonymous struct and provides access to the entire service interface through the mock. We have only a single method, DescribeVpcs, as that is the only API call made in ListVpcCidrBlocks().

When the DescribeVpcs call is made through the mock, it will hit this method, and then be returned whatever value is stored in the DescribeVpcsOutput field.

Next, the static data that we are going to return from the mock must be defined:

Finally, the key here is that our mockedEC2 struct should be used in place of the concrete EC2 client:

This is the mechanism by which the API call is routed to our mock and not to AWS. Since the method of the mock returns a pointer to the DescribeVpcsOutput field, we can change the output returned based on the test case.

❯ go test -v
=== RUN TestListVpcCidrBlocks
=== RUN TestListVpcCidrBlocks/SingleCidrAssociation
=== RUN TestListVpcCidrBlocks/MultipleCidrAssociation
--- PASS: TestListVpcCidrBlocks (0.00s)
--- PASS: TestListVpcCidrBlocks/SingleCidrAssociation (0.00s)
--- PASS: TestListVpcCidrBlocks/MultipleCidrAssociation (0.00s)
PASS
ok github.com/brandonstrohmeyer/aws-mock-go 0.234s

The full example code can be found on github.

--

--