LoginSignup
8
6

More than 3 years have passed since last update.

GoのユニットテストでAWS SDKのモックをする

Last updated at Posted at 2019-07-01

Goのユニットテストでどうしてもaws-sdkを叩くレイヤーのtestがしたくなったとき、localstackなど実際のawsを模した機能を用意できればそれが一番だが、たいてい大掛かりになってしまう。
goのaws-sdkには大抵interfaceが用意されているので、これをmockでうまく使うとなかなか良かった。

そもそもDIの基本になるが、なるべくs3.S3などのstructそのものでなく、このようなinterfaceへの依存を持っておくことで簡単にmockが可能になるので、設計の際に留意しておく。

例えば、s3であれば S3APIというinterfaceがある。

今回はこういうものを用意した。

package aws_sdk

import (
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/request"
    "github.com/aws/aws-sdk-go/service/s3"
)

type S3SdkMockImpl struct {
    //テストごとに差し替え可能にする
    ListObjects_Mock func(*s3.ListObjectsInput) (*s3.ListObjectsOutput, error)
    HeadObject_Mock  func(*s3.HeadObjectInput) (*s3.HeadObjectOutput, error)
}

// auto generated by jetbrains
func (S3SdkMockImpl) AbortMultipartUpload(*s3.AbortMultipartUploadInput) (*s3.AbortMultipartUploadOutput, error) {
    panic("implement me")
}

func (S3SdkMockImpl) AbortMultipartUploadWithContext(aws.Context, *s3.AbortMultipartUploadInput, ...request.Option) (*s3.AbortMultipartUploadOutput, error) {
    panic("implement me")
}

func (S3SdkMockImpl) AbortMultipartUploadRequest(*s3.AbortMultipartUploadInput) (*request.Request, *s3.AbortMultipartUploadOutput) {
    panic("implement me")
}

func (S3SdkMockImpl) CompleteMultipartUpload(*s3.CompleteMultipartUploadInput) (*s3.CompleteMultipartUploadOutput, error) {
    panic("implement me")
}

func (S3SdkMockImpl) CompleteMultipartUploadWithContext(aws.Context, *s3.CompleteMultipartUploadInput, ...request.Option) (*s3.CompleteMultipartUploadOutput, error) {
    panic("implement me")
}

...

// ダミーが刺されていれば使う
func (self *S3SdkMockImpl) HeadObject(in *s3.HeadObjectInput) (*s3.HeadObjectOutput, error) {
    if self.HeadObject_Mock != nil {
        return self.HeadObject_Mock(in)
    }
    panic("implement me")
}

....
func (self *S3SdkMockImpl) ListObjects(in *s3.ListObjectsInput) (*s3.ListObjectsOutput, error) {

    if self.ListObjects_Mock != nil {
        return self.ListObjects_Mock(in)
    }

    panic("implement me")
}

interfaceを埋めるメソッド自体はGolang等のIDEですぐにgenerateできるが、テストケースごとに生成するのも見通しが悪いため、必要なメソッドごとに差し替え可能にし、使いまわせるようにした。
使用側はこうなる。

s3SdkMockTestCase01 := aws_sdk.S3SdkMockImpl{
    ListObjects_Mock: func(input *s3.ListObjectsInput) (output *s3.ListObjectsOutput, e error) {

        return &s3.ListObjectsOutput{
            ... //自由に返す
        } , nil
    },
}

s3SdkMockTestCase02 := aws_sdk.S3SdkMockImpl{
    ListObjects_Mock: func(input *s3.ListObjectsInput) (output *s3.ListObjectsOutput, e error) {

        return &s3.ListObjectsOutput{
            ... //自由に返す
        } , nil
    },
}

s3DataRepoisitoryForTest01 := NewS3DataRepository(s3SdkMockTestCase01)
s3DataRepoisitoryForTest02 := NewS3DataRepository(s3SdkMockTestCase02)

場合に応じて差し替えが簡単になり、使いまわせるようにもなった。

追記

interface自体を埋め込んであげるともっといいですね...

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6