AWS SDK for Go v2 を使用したユニットテスト - AWS SDK for Go v2

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

AWS SDK for Go v2 を使用したユニットテスト

アプリケーションで SDK を使用する場合は、アプリケーションのユニットテスト用に SDK をモック化します。SDK をモック化すると、SDK の内部動作ではなく、アプリケーションのテスト対象に集中できます。

モック化をサポートするには、具象型 (サービスクライアントやページネーター、ウェーターなど) ではなく、Go のインターフェイス (s3.Client など) を使用します。これにより、依存関係インジェクションなどのパターンを使用して、アプリケーションロジックをテストできるようになります。

クライアントオペレーションのモック化

この例では、S3GetObjectAPIGetObjectFromS3 関数に必要な一連の Amazon S3 API オペレーションを定義するインターフェイスです。S3GetObjectAPI は、Amazon S3 クライアントの GetObject メソッドによって実装されます。

import "context" import "github.com/aws/aws-sdk-go-v2/service/s3" // ... type S3GetObjectAPI interface { GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) } func GetObjectFromS3(ctx context.Context, api S3GetObjectAPI, bucket, key string) ([]byte, error) { object, err := api.GetObject(ctx, &s3.GetObjectInput{ Bucket: &bucket, Key: &key, }) if err != nil { return nil, err } defer object.Body.Close() return ioutil.ReadAll(object.Body) }

GetObjectFromS3 関数をテストするには、mockGetObjectAPI を使用して S3GetObjectAPI インターフェイスを実装します。その後、mockGetObjectAPI 型を使用して、サービスクライアントから返される出力とエラーレスポンスをモック化します。

import "testing" import "github.com/aws/aws-sdk-go-v2/service/s3" // ... type mockGetObjectAPI func(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) func (m mockGetObjectAPI) GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) { return m(ctx, params, optFns...) } func TestGetObjectFromS3(t *testing.T) { cases := []struct { client func(t *testing.T) S3GetObjectAPI bucket string key string expect []byte }{ { client: func(t *testing.T) S3GetObjectAPI { return mockGetObjectAPI(func(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) { t.Helper() if params.Bucket == nil { t.Fatal("expect bucket to not be nil") } if e, a := "fooBucket", *params.Bucket; e != a { t.Errorf("expect %v, got %v", e, a) } if params.Key == nil { t.Fatal("expect key to not be nil") } if e, a := "barKey", *params.Key; e != a { t.Errorf("expect %v, got %v", e, a) } return &s3.GetObjectOutput{ Body: ioutil.NopCloser(bytes.NewReader([]byte("this is the body foo bar baz"))), }, nil }) }, bucket: "amzn-s3-demo-bucket>", key: "barKey", expect: []byte("this is the body foo bar baz"), }, } for i, tt := range cases { t.Run(strconv.Itoa(i), func(t *testing.T) { ctx := context.TODO() content, err := GetObjectFromS3(ctx, tt.client(t), tt.bucket, tt.key) if err != nil { t.Fatalf("expect no error, got %v", err) } if e, a := tt.expect, content; bytes.Compare(e, a) != 0 { t.Errorf("expect %v, got %v", e, a) } }) } }

ページネーターのモック化

サービスクライアントと同様に、ページネーターも Go インターフェイスを定義することでモック化できます。このインターフェイスはアプリケーションのコードから使用されます。これにより、アプリケーションの実行時には SDK の実装を使用し、テスト時にはモック実装を利用できます。

次の例では、ListObjectsV2PagerCountObjects 関数に必要な Amazon S3 ListObjectsV2Paginator の動作を定義するインターフェイスです。

import "context" import "github.com/aws/aws-sdk-go-v2/service/s3" // ... type ListObjectsV2Pager interface { HasMorePages() bool NextPage(context.Context, ...func(*s3.Options)) (*s3.ListObjectsV2Output, error) } func CountObjects(ctx context.Context, pager ListObjectsV2Pager) (count int, err error) { for pager.HasMorePages() { var output *s3.ListObjectsV2Output output, err = pager.NextPage(ctx) if err != nil { return count, err } count += int(output.KeyCount) } return count, nil }

CountObjects をテストするには、ListObjectsV2Pager インターフェイスを実装するように mockListObjectsV2Pager 型を作成します。その後、mockListObjectsV2Pager を使用して、サービスオペレーションのページネーターからの出力とエラーレスポンスのページング動作を再現します。

import "context" import "fmt" import "testing" import "github.com/aws/aws-sdk-go-v2/service/s3" // ... type mockListObjectsV2Pager struct { PageNum int Pages []*s3.ListObjectsV2Output } func (m *mockListObjectsV2Pager) HasMorePages() bool { return m.PageNum < len(m.Pages) } func (m *mockListObjectsV2Pager) NextPage(ctx context.Context, f ...func(*s3.Options)) (output *s3.ListObjectsV2Output, err error) { if m.PageNum >= len(m.Pages) { return nil, fmt.Errorf("no more pages") } output = m.Pages[m.PageNum] m.PageNum++ return output, nil } func TestCountObjects(t *testing.T) { pager := &mockListObjectsV2Pager{ Pages: []*s3.ListObjectsV2Output{ { KeyCount: 5, }, { KeyCount: 10, }, { KeyCount: 15, }, }, } objects, err := CountObjects(context.TODO(), pager) if err != nil { t.Fatalf("expect no error, got %v", err) } if expect, actual := 30, objects; expect != actual { t.Errorf("expect %v, got %v", expect, actual) } }