Пусть функция Go принимает разные структуры в качестве входных данных с методами
Я довольно новичок в Go и, похоже, не могу сосредоточиться на его системе struct/interface при попытке создать фиктивный объект для модульного тестирования загрузчика 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 и mocked mockUploader в качестве допустимых аргументов и распознавала оба метода Upload. Я попытался установить тип перед вызовом метода Upload, но это только дало другую ошибку. Может кто-нибудь помочь, и скажите мне, что я делаю не так?
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
}
Вы должны дать обоим типам общий набор методов if. Общий означает, что имя, типы аргументов и возвращаемые типы должны быть идентичны. Поскольку вы не можете изменить реальную реализацию, ваша единственная возможность - использовать макет, имитирующий тип 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) {
}