DEV Community

Daniel Quackenbush
Daniel Quackenbush

Posted on

Mocking the AWS SDK With Go

Mocking a client library is a common technique when building test-driven development. In golang, this can be done by creating structs that implement interfaces and then override the methods you are trying to mock. This example of mocking can be done with any method, but for this post, I will use AWS Organizations to demonstrate.

Implementation Code

First, we want to create a struct that will be used as the methods' instance. As you see below, we are going to implement the interface of the SDK provided organizations API:

type Organizations struct {
    Client organizationsiface.OrganizationsAPI
}
Enter fullscreen mode Exit fullscreen mode

Then once we have defined that, we will have to instantiate the method we want to implement, which for the main implementation will be a pass-through for the method used by the client.

func (s *Organizations) ListAccounts(in *organizations.ListAccountsInput) (*organizations.ListAccountsOutput, error) {
    result, err := s.Client.ListAccounts(in)
    return result, err
}
Enter fullscreen mode Exit fullscreen mode

Finally, we will want to create a method we will test. We must parameterize the struct to help our test define which method to use.

func WhatAreMyAccounts(client *Organizations) (*organizations.ListAccountsOutput, error) {
    return client.ListAccounts(
        &organizations.ListAccountsInput{
            MaxResults: aws.Int64(5),
            NextToken:  nil,
        },
    )
}
Enter fullscreen mode Exit fullscreen mode

Test Code

For testing, we will create our Mock struct, followed by our method override, and then wrap that within a given test. The MockOrganization struct will implement an interface, which we can later utilize as the organization's client.

type MockedOrganizations struct {
    organizationsiface.OrganizationsAPI
}
Enter fullscreen mode Exit fullscreen mode

For the test, we want to guarantee a response. Therefore, we will define a separate implementation of the organization interface, of which we will return a constant value for the test suite.

func (m *MockedOrganizations) ListAccounts(in *organizations.ListAccountsInput) (*organizations.ListAccountsOutput, error) {
    return &organizations.ListAccountsOutput{
        Accounts: []*organizations.Account{
            {
                Arn:   aws.String(""),
                Email: aws.String("test1@example.com"),
                Id:    aws.String("234567890"),
                Name:  aws.String("test-1"),
            },
            {
                Arn:   aws.String(""),
                Email: aws.String("test2@example.com"),
                Id:    aws.String("123456789"),
                Name:  aws.String("test-2"),
            },
        },
    }, nil
}
Enter fullscreen mode Exit fullscreen mode

On the test code, by using our mocked interface, we can create an Organizations object. The application's function then calls our mocked response to yield the result to test.

func TestListAccounts(t *testing.T) {
    test := Organizations{
        Client: &MockedOrganizations{},
    }
    resp, err := WhatAreMyAccounts(&test)
    assert.Equal(t, len(resp.Accounts), 2)
    assert.NoError(t, err)
}
Enter fullscreen mode Exit fullscreen mode

Results

➜  main go test -v
=== RUN   TestListAccounts
--- PASS: TestListAccounts (0.00s)
PASS
ok      main/cmd/main   0.117s
Enter fullscreen mode Exit fullscreen mode

To see the full code, check out the below gists:

Top comments (1)

Collapse
 
grahamcox82 profile image
Graham Cox

This is great, and a helpful introduction to testing the AWS SDK in my code.

However, I can't work out how to test the error cases? For example, how would I make my mock client return an AWSOrganizationsNotInUseException? As best I can tell this isn't a constructable type so I can't construct and return it the way I would with ListAccountsOutput.