Skip to content

Instantly share code, notes, and snippets.

@jboursiquot
Last active May 26, 2021 16:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jboursiquot/331b69a04ed1124d7a82fdc47ee54abc to your computer and use it in GitHub Desktop.
Save jboursiquot/331b69a04ed1124d7a82fdc47ee54abc to your computer and use it in GitHub Desktop.
Working with Interfaces (sample solution for https://gist.github.com/jboursiquot/1784ad14fbd2037197400053c3f41b84)
package mypackage
import (
"context"
"fmt"
"os"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
)
// Note the key takeaway here:
// We don't need to mock out the entire DynamoDB client interface, just the methods we need.
// We don't even need to use the interface AWS provides for DynamoDB SDK, again, just the behavior we need.
type ddbClient interface {
PutItemWithContext(ctx aws.Context, input *dynamodb.PutItemInput, opts ...request.Option) (*dynamodb.PutItemOutput, error)
}
// DynamoDBSaver interacts with DynamoDB.
type DynamoDBSaver struct {
Client ddbClient
}
// Person captures demographics.
type Person struct {
Name string
}
// Save saves.
func (s *DynamoDBSaver) Save(ctx context.Context, p *Person) error {
item, err := dynamodbattribute.MarshalMap(p)
if err != nil {
return fmt.Errorf("failed to marshal shoutout for storage: %s", err)
}
input := &dynamodb.PutItemInput{
Item: item,
TableName: aws.String(os.Getenv("TABLE_NAME")),
}
_, err = s.Client.PutItemWithContext(ctx, input)
return err
}
package mypackage_test
import (
"context"
"errors"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/jboursiquot/sandbox/mypackage"
)
// Note how our testClient does not need to depend on the `mypackage.ddbClient` interface here.
type testClient struct {
output *dynamodb.PutItemOutput
err error
}
// We only need our client to satisfy just the bits we need from the DynamoDB client interface implicitly.
func (c *testClient) PutItemWithContext(ctx aws.Context, input *dynamodb.PutItemInput, opts ...request.Option) (*dynamodb.PutItemOutput, error) {
return c.output, c.err
}
func TestDynamoDBSaver(t *testing.T) {
tests := map[string]struct {
person *mypackage.Person
err error // We can even mock out errors to test sad paths
}{
"happy path": {
person: &mypackage.Person{Name: "Johnny"},
},
"sad path": {
person: &mypackage.Person{Name: "Johnny"},
err: errors.New("failed to save"),
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
client := &testClient{err: tc.err}
saver := &mypackage.DynamoDBSaver{Client: client}
ctx := context.Background()
if err := saver.Save(ctx, tc.person); err != tc.err {
t.Errorf("expected %v but got %v", tc.err, err)
}
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment