使用 AWS SDK for Go v2 進行單位測試 - AWS SDK for Go v2

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

使用 AWS SDK for Go v2 進行單位測試

在應用程式中使用 SDK 時,您會想要模擬應用程式單元測試的 SDK。模擬 SDK 可讓您的測試專注於您想要測試的內容,而不是 SDK 的內部內容。

若要支援模擬,請使用 Go 介面,而非具體服務用戶端、分頁程式和等待程式類型,例如 s3.Client。這可讓您的應用程式使用相依性注入等模式來測試應用程式邏輯。

模擬用戶端操作

在此範例中, S3GetObjectAPI 是定義 GetObjectFromS3函數所需 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 界面來模擬分頁器。該界面將由應用程式的程式碼使用。這可讓軟體開發套件的實作在應用程式執行時使用,以及用於測試的模擬實作。

在下列範例中, ListObjectsV2Pager 是一個界面,定義 CountObjects函數所需的 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,請建立 mockListObjectsV2Pager類型以滿足ListObjectsV2Pager界面定義。然後使用 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) } }