Go函数接受不同的结构作为输入方法

时间:2018-04-09 22:37:51

标签: unit-testing go types mocking assertions

我是Go的新手,在尝试为AWS s3manager上传器的单元测试创​​建模拟对象时,我似乎无法绕过它的结构/接口系统。

在我的包文件中,我有:

package uploader

import (
        "fmt"
        "github.com/aws/aws-sdk-go/aws"
        "github.com/aws/aws-sdk-go/aws/session"
        "github.com/aws/aws-sdk-go/service/s3/s3manager"
        "os"
)

func GetS3Uploader() *s3manager.Uploader {
    conf := aws.Config{Region: aws.String("eu-west-1")}
    sess := session.New(&conf)
    uploader := s3manager.NewUploader(sess)
    return uploader
}

func uploadFile(uploader interface{}) {

    uploader.Upload(&s3manager.UploadInput{
       Bucket: aws.String("A"),
       Key:    aws.String("B"),
       Body:   bytes.NewReader([]byte("C")),
    })

}

并且在匹配的uploader_test.go中有以下代码,包含模拟对象:

package uploader_test

import (
    . "github.com/onsi/ginkgo"
    . "github.com/something/reponame/uploader"
    "github.com/aws/aws-sdk-go/service/s3/s3manager"
)

type mockUploadOutput struct {
    Location  string
    VersionID *string
    UploadID  string
}

type mockUploader struct {
}

func (*mockUploader) Upload(input *s3manager.UploadInput) (mockUploadOutput, error) {
    versionID := "TESTVERSION"
    mockUploadResponse := mockUploadOutput{
        Location:  "TESTLOCATION",
        VersionID: &versionID,
        UploadID:  "TESTUPLOADID",
    }
    return mockUploadResponse, nil
}

var _ = Describe("Reportuploader", func() {

    var (
        mockUp mockUploader
    )

    Describe("Upload()", func() {
        Context("With mocked uploader object", func() {
            It("should return the predefined mockUploadResponse", func() {

                uploadFile(mockUp)

            })
        })
    })
})

但是当我尝试运行它时,我收到以下错误:

uploader.Upload undefined (type interface {} is interface with no methods)

我的目标是让uploadFile函数接受* s3manager.Uploader对象和模拟的mockUploader作为有效参数,并识别它们的Upload方法。我在调用Upload方法之前尝试断言该类型,但这只会产生不同的错误。任何人都可以帮忙,并告诉我我做错了什么?

2 个答案:

答案 0 :(得分:2)

您将类型签名设置为func uploadFile(uploader interface{}) {,这意味着它接受任何内容作为参数,但您无法在uploader上调用任何方法,因为您的类型签名interface{}没有任何方法在它上面。

看起来你想要做的是:

type Uploader interface {
    Upload(input *s3manager.UploadInput) 
}

func uploadFile(uploader Uploader) {
    uploader.Upload(&s3manager.UploadInput{
        Bucket: aws.String("A"),
        Key:    aws.String("B"),
        Body:   bytes.NewReader([]byte("C")),
    })
}

但是你需要匹配函数签名,看起来其中一个有:

(mockUploadOutput, error) 

和另一个

(*s3manager.UploadOutput, error)

所以,我可能会做的只是从你的模拟中返回一个真实的(*s3manager.UploadOutput, error)

func (*mockUploader) Upload(input *s3manager.UploadInput) (*s3manager.UploadOutput, error) {
  versionID := "TESTVERSION"
  mockUploadResponse := &s3manager.UploadOutput{
      Location:  "TESTLOCATION",
      VersionID: &versionID,
      UploadID:  "TESTUPLOADID",
  }
  return mockUploadResponse, nil
}

答案 1 :(得分:1)

如果方法,你必须给两种类型一个共同的集合。 Common表示名称,参数类型和返回类型必须相同。由于您无法更改实际实现,因此您唯一的选择是让模拟模仿s3类型。 s3的上传方法定义如下:

Upload(input *s3manager.UploadInput, options ...func(*s3manager.Uploader)) (*s3manager.UploadOutput, error)

你的模拟必须有完全相同的方法:

func (*mockUploader) Upload(input *s3manager.UploadInput, options ...func(*s3manager.Uploader)) (*s3manager.UploadOutput, error) {
    // Implementation
}

请注意,您无法更改返回类型。

现在两种类型共享一个您可以依赖的接口:

type Uploader interface {
    Upload(input *s3manager.UploadInput, options ...func(*s3manager.Uploader)) (*s3manager.UploadOutput, error)
}

func uploadFile(uploader Uploader) {
}