Last active
May 26, 2021 16:11
-
-
Save jboursiquot/331b69a04ed1124d7a82fdc47ee54abc to your computer and use it in GitHub Desktop.
Working with Interfaces (sample solution for https://gist.github.com/jboursiquot/1784ad14fbd2037197400053c3f41b84)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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