Пусть функция 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) {
}
Другие вопросы по тегам