如何模拟 s3Client 以便可以在 Golang 测试中运行 GetObject()?

mar*_*ins 6 unit-testing dependency-injection amazon-s3 go amazon-web-services

我有一个接受路径和 S3Client 的函数: ListObjects(folder, s3Client) 我希望它接受生产中的“真实”客户端。即:s3.New(session.New() 但是当我运行测试时,我希望它接受我的模拟版本,这样我们就不会在测试中与 AWS 对话。我实现此目的的方法是创建一个与真实 S3Client 和我的模拟版本相匹配的界面。

这是最好的方法还是我错过了什么?

首先,我使用我计划模拟的函数定义了接口。

  package core

  import "github.com/aws/aws-sdk-go/service/s3"

  type MyS3ClientInterface interface {
         ListObjects(input *s3.ListObjectsInput) (*s3.ListObjectsOutput, error)
  }
Run Code Online (Sandbox Code Playgroud)

GetObjectsFromS3 的实现如下:

// GetObjectsFromS3 returns a list of objects found in provided path on S3
func GetObjectsFromS3(path string, s3Client core.MyS3ClientInterface) ([]*core.Asset, error) {
      // build input variable
      result, err := s3Client.ListObjects(input)
      // cut..
}
Run Code Online (Sandbox Code Playgroud)

这是我的 s3Client.ListObjects 模拟版本

  package test_awstools

  import (
          "github.com/aws/aws-sdk-go/service/s3"
  )

  type MyS3Client struct{}

  func (m MyS3Client) ListObjects(input *s3.ListObjectsInput) (*s3.ListObjectsOutput, error) {
          output := &s3.ListObjectsOutput{}
          return output, nil
  }

  func (m MyS3Client) GetObject(input *s3.GetObjectInput) (*s3.GetObjectOutput, error) {
          // output := &s3.GetObject()
          return nil, nil
  }
Run Code Online (Sandbox Code Playgroud)

内部/awstools/bucket_test.go

  1 package awstools
  2
  3 import (
  4         "fmt"
  5         "testing"
  6
  7         "internal/test_awstools"
  8 )
  9
 10 func TestListObjects(t *testing.T) {
 11         folder := "docs"
            // Use my mocked version of the S3 client.
 12         s3Client := &test_awstools.MyS3Client{}
            // And I pass that along to the ListObject function
 13         objects, err := ListObjects(folder, s3Client)
 14         if err != nil {
 15                 t.Error(err)
 16         }
 17         fmt.Println(objects)
 18 }
Run Code Online (Sandbox Code Playgroud)

小智 1

我建议的一个小改进是允许您的模拟结构接受一个函数,这样它可以在测试之间有所不同。像这样的东西:

  package test_awstools

  import (
          "github.com/aws/aws-sdk-go/service/s3"
  )

  type MyS3Client struct {
    ListObjectsFunc func(*s3.ListObjectsInput) (*s3.ListObjectsOutput, error)
    GetObjectFunc func(*s3.GetObjectInput) (*s3.GetObjectOutput, error)
  }

  func (m *MyS3Client) ListObjects(input *s3.ListObjectsInput) (*s3.ListObjectsOutput, error) {
    return m.ListObjectsFunc(input)
  }

  func (m *MyS3Client) GetObject(input *s3.GetObjectInput) (*s3.GetObjectOutput, error) {
    return m.GetObjectFunc(input)
  }
Run Code Online (Sandbox Code Playgroud)

现在,在您的测试中,您可以*Func在结构上设置值,以便您的测试对存根行为更具声明性:

func TestListObjects(t *testing.T) {
  folder := "docs"

  // Use my mocked version of the S3 client.
  s3Client := &test_awstools.MyS3Client{}

  // Set the ListObjectsFunc to behave the way you want
  s3Client.ListObjectsFunc = func(input *s3.ListObjectsInput) (*s3.ListObjectsOutput, error) {
    output := &s3.ListObjectsOutput{}
    return output, nil
  }

  // Make the call to list objects (which is now mocked)
  objects, err := s3Client.ListObjects(folder, s3Client)

  // Do your assertions
  if err != nil {
    t.Error(err)
  }
  fmt.Println(objects)
}
Run Code Online (Sandbox Code Playgroud)