让 Go 函数接受不同的结构作为方法的输入
Have a Go function accept different structs as input with methods
我是 Go 的新手,在尝试为 AWS s3manager 上传器的单元测试创建模拟对象时,我似乎无法理解它的 struct/interface 系统。
在我的包文件中我有:
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 方法之前断言类型,但这只给出了不同的错误。谁能帮忙,告诉我我做错了什么?
您将类型签名设置为 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)
所以,我可能会做的只是 return 来自您的模拟的真实 (*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
}
您必须为这两种类型提供一个通用的集合 if 方法。 Common 意味着名称、参数类型和 return 类型必须相同。由于您无法更改实际实现,因此您唯一的选择是让模拟模仿 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
}
请注意,您无法更改 return 类型。
现在这两种类型共享一个您可以依赖的接口:
type Uploader interface {
Upload(input *s3manager.UploadInput, options ...func(*s3manager.Uploader)) (*s3manager.UploadOutput, error)
}
func uploadFile(uploader Uploader) {
}
我是 Go 的新手,在尝试为 AWS s3manager 上传器的单元测试创建模拟对象时,我似乎无法理解它的 struct/interface 系统。
在我的包文件中我有:
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 方法之前断言类型,但这只给出了不同的错误。谁能帮忙,告诉我我做错了什么?
您将类型签名设置为 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)
所以,我可能会做的只是 return 来自您的模拟的真实 (*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
}
您必须为这两种类型提供一个通用的集合 if 方法。 Common 意味着名称、参数类型和 return 类型必须相同。由于您无法更改实际实现,因此您唯一的选择是让模拟模仿 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
}
请注意,您无法更改 return 类型。
现在这两种类型共享一个您可以依赖的接口:
type Uploader interface {
Upload(input *s3manager.UploadInput, options ...func(*s3manager.Uploader)) (*s3manager.UploadOutput, error)
}
func uploadFile(uploader Uploader) {
}