【问题标题】:Have a Go function accept different structs as input with methods让 Go 函数接受不同的结构作为方法的输入
【发布时间】:2018-09-19 10:16:18
【问题描述】:

我对 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 方法之前断言类型,但这只会给出不同的错误。谁能帮忙,告诉我我做错了什么?

【问题讨论】:

    标签: unit-testing go types mocking assertions


    【解决方案1】:

    你必须给这两种类型一个共同的集合 if 方法。 Common 意味着名称、参数类型和返回类型必须相同。由于您无法更改实际实现,因此您唯一的选择是让 mock 模仿 s3 类型。 s3的Upload方法定义如下:

    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) {
    }
    

    【讨论】:

      【解决方案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
      }
      

      【讨论】:

      • 这有助于理解我所缺少的内容,但现在我似乎遇到了另一个错误:runtime error: invalid memory address or nil pointer dereference,堆栈跟踪指向此开始的行:uploader.Upload(&s3manager.UploadInput{
      • 好的,我通过删除 func (*mockUploader) Upload(input *s3manager.UploadInput) 中 mockUploader 前面的星号并接受 @peter 的建议并将完整的参数列表与 options ...func(*s3manager.Uploader) 部分一起放入其中来让它工作。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-26
      • 2022-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多