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
}
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
}
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,
},
)
}
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
}
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("[email protected]"),
Id: aws.String("234567890"),
Name: aws.String("test-1"),
},
{
Arn: aws.String(""),
Email: aws.String("[email protected]"),
Id: aws.String("123456789"),
Name: aws.String("test-2"),
},
},
}, nil
}
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)
}
Results
➜ main go test -v
=== RUN TestListAccounts
--- PASS: TestListAccounts (0.00s)
PASS
ok main/cmd/main 0.117s
To see the full code, check out the below gists:
package main | |
import ( | |
"github.com/aws/aws-sdk-go/aws" | |
"github.com/aws/aws-sdk-go/service/organizations" | |
"github.com/aws/aws-sdk-go/service/organizations/organizationsiface" | |
) | |
type Organizations struct { | |
Client organizationsiface.OrganizationsAPI | |
} | |
func (s *Organizations) ListAccounts(in *organizations.ListAccountsInput) (*organizations.ListAccountsOutput, error) { | |
result, err := s.Client.ListAccounts(in) | |
return result, err | |
} | |
func WhatAreMyAccounts(client *Organizations) (*organizations.ListAccountsOutput, error) { | |
return client.ListAccounts( | |
&organizations.ListAccountsInput{ | |
MaxResults: aws.Int64(5), | |
NextToken: nil, | |
}, | |
) | |
} |
package main | |
import ( | |
"testing" | |
"github.com/aws/aws-sdk-go/aws" | |
"github.com/aws/aws-sdk-go/service/organizations" | |
"github.com/aws/aws-sdk-go/service/organizations/organizationsiface" | |
"github.com/stretchr/testify/assert" | |
) | |
type MockedOrganizations struct { | |
organizationsiface.OrganizationsAPI | |
} | |
func (m *MockedOrganizations) ListAccounts(in *organizations.ListAccountsInput) (*organizations.ListAccountsOutput, error) { | |
return &organizations.ListAccountsOutput{ | |
Accounts: []*organizations.Account{ | |
{ | |
Arn: aws.String(""), | |
Email: aws.String("[email protected]"), | |
Id: aws.String("234567890"), | |
Name: aws.String("test-1"), | |
}, | |
{ | |
Arn: aws.String(""), | |
Email: aws.String("[email protected]"), | |
Id: aws.String("123456789"), | |
Name: aws.String("test-2"), | |
}, | |
}, | |
}, nil | |
} | |
func TestListAccounts(t *testing.T) { | |
test := Organizations{ | |
Client: &MockedOrganizations{}, | |
} | |
resp, err := WhatAreMyAccounts(&test) | |
assert.Equal(t, len(resp.Accounts), 2) | |
assert.NoError(t, err) | |
} |