Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mock external dependencies in golang

I have a program in go that connects to AWS S3 and gets a file. I'd like to write some tests for it, but I'd like to know, more generally, how to do these mocks in Golang. I know there are some libraries to create mocks but if I remember correctly I read someone suggesting using only standard libraries for unit tests was the best way to go.

So, how would you test a function like this?

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
}

Thank you!

like image 748
Sync Avatar asked Aug 11 '18 11:08

Sync


People also ask

What is mock dependency?

Mocking is a way to replace a dependency in a unit under test with a stand-in for that dependency. The stand-in allows the unit under test to be tested without invoking the real dependency.

Should you mock all dependencies?

You should only mock the behaviour of objects which are necessary for the test to pass. Everything else should be replaced by a dummy or null (if possible). Null values are a great indicator that something is unused.


1 Answers

One way to do it is to inject the dependencies in your structure, like such:

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,
    }

    // ...
}

This allows you to replace those functions with whatever you want for testing, without the need of any testing framework.

Then, the testing function would look something like this:

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
}

You could improve it by creating a S3InputMock type instead of reusing the base one, both would implement the S3Inputer interface and your mock could have attributes allowing it to help you with testing. For example, it could count the number of times a function is called, store the arguments it received, have its methods behave differently depending on the attributes you set for easier testing, etc.

like image 121
Ullaakut Avatar answered Oct 15 '22 14:10

Ullaakut