在golang中模拟外部依赖项

12

我有一个Go程序,连接到AWS S3并获取文件。我想为它编写一些测试,但更普遍地说,我想知道如何在Golang中进行这些mock测试。我知道有一些库可以创建mocks,但如果我没记错的话,我曾经看到有人建议只使用标准库进行单元测试是最好的方法。

那么,你会如何测试这样一个函数?

func (s S3Input) Sample(key string) ([]byte, error) {
    var buf []byte
    waBuf := aws.NewWriteAtBuffer(buf)

    _, err := s.Downloader.Download(
        waBuf,
        &s3.GetObjectInput{
            Bucket: aws.String(s.Bucket),
            Key:    aws.String(key),
        },
    )
    if err != nil {
        return nil, err
    }

    return buf, nil
}
谢谢你!

4
如果我看你的代码,我不知道你想要测试什么逻辑。在Go中的通用解决方案是将一个接口作为输入。然后你可以将依赖项传递到你的代码和测试中的模拟对象到该函数中。 - apxp
1
这个函数对于"test"没有任何作用,所以为什么要费心呢。 - Volker
1
你说得对,这段代码没有太多需要测试的地方。我的问题可能更加通用,比如:“在使用Go进行单元测试时,有哪些最佳实践?”在Ruby中,你可以使用doubles,那么在Go中是否需要类似的东西,或者有更直接的方法? - Sync
1个回答

11
一种方法是将依赖项注入到您的结构中,例如:

通过这样的方式:

type S3Inputer interface {
    NewWriteAtBuffer(buf []byte) *aws.WriteAtBuffer
    String(v string) *string
}

type S3Input struct {
    newWriteAtBufferFunc func(buf []byte) *aws.WriteAtBuffer
    stringFunc           func(v string) *string
}

func (s *S3Input) NewWriteAtBuffer(buf []byte) *WriteAtBuffer {
    return s.newWriteAtBufferFunc(buf)
}

func (s *S3Input) String(v string) *string {
    return s.stringFunc(v)
}

func (s S3Input) Sample(key string) ([]byte, error) {
    var buf []byte
    waBuf := s.NewWriteAtBuffer(buf)

    _, err := s.Downloader.Download(
        waBuf,
        &s3.GetObjectInput{
            Bucket: s.String(s.Bucket),
            Key:    s.String(key),
        },
    )
    if err != nil {
        return nil, err
    }

    return buf, nil
}

func main() {
    s := &S3Input{
        StringFunc:           aws.String,
        NewWriteAtBufferFunc: aws.NewWriteAtBuffer,
    }

    // ...
}

这样做可以让你在测试时替换掉那些函数,而不需要任何测试框架。接下来,测试函数可能会像这样:
func (s S3Input) TestSample(t *testing.T) {
    s3Mock := &S3Input{
        StringFunc:           (func (v string) *string {
            return nil
        }),
        NewWriteAtBufferFunc: (func (buf []byte) *aws.WriteAtBuffer {
            return nil
        }),
    }

    res, err := s3Mock.Sample(...) //
    // asserts & error checks
}

你可以创建一个S3InputMock类型来改进它,而不是重复使用基本类型,两者都将实现S3Inputer接口,你的模拟可以具有属性,可帮助你进行测试。例如,它可以计算函数被调用的次数,存储它接收到的参数,根据你为更轻松的测试设置的属性使其方法行为不同等。

谢谢,这可能是一种测试的方式,尽管正如其他人所说,这个特定的代码片段实际上不需要任何真正的测试,因为基本上没有逻辑。 - Sync
是的,但这种方法不仅适用于您的代码,而且是一种测试依赖外部依赖项的东西的一般方式 :) 如果需要更多帮助,请告诉我! - Ullaakut

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接