Search code examples
goamazon-s3goland

Mock awsEventStream


I have a function as below, I have tried mocking the aws s3 selectobjectcontentoutput but all in vain. I have even tried s3fice which says: Package s3iface provides an interface to enable mocking the Amazon Simple Storage Service service client for testing your code.

    func awsEventStream(resp *s3.SelectObjectContentOutput) string {

    ---

    return awsResponse

}

I am trying to write test case for the above as below, there are various methopds in s3 but I eed to mock only the method used in my main class.

    package main

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

type SelectObjectContentOutput struct {
    s3.SelectObjectContentOutput
}

func (m *SelectObjectContentOutput) EventStream(input *s3.SelectObjectContentInput) (*s3.SelectObjectContentOutput, error) {
    output := &s3.SelectObjectContentOutput{
        EventStream: nil,
    }
    return output, nil
}

func TestAwsEventStream(t *testing.T) {
    mockSvc := &SelectObjectContentOutput{}
    awsEventStream(&mockSvc.SelectObjectContentOutput)

}

Solution

  • Not able to mock SelectObjectContentOutput. We are getting the EventStream as null.

    When creating s3.SelectObjectContentOutput, you need to specify the EventStream attribute. From reading the s3 SelectObjectContentEventStream docs:

    For testing and mocking the event stream this type should be initialized via the NewSelectObjectContentEventStream constructor function. Using the functional options to pass in nested mock behavior.

    The docs also include example on how to use NewSelectObjectContentEventStream.

    es := NewSelectObjectContentEventStream(func(o *SelectObjectContentEventStream){
        o.Reader = myMockStreamReader
        o.StreamCloser = myMockStreamCloser
    })
    

    For this question, we can ignore StreamCloser by setting it to empty ReadCloser.

    Setting the Reader SelectObjectContentEventStreamReader will be trickier because it is an interface and from the look of it, s3 does not provide a direct way to construct the reader. Thought we can just create a type that implements it:

    type stubSelectObjectContentEventStreamReader struct {
        StreamEvents <-chan s3.SelectObjectContentEventStreamEvent
        Error        error
    }
    
    func (s stubSelectObjectContentEventStreamReader) Events() <-chan s3.SelectObjectContentEventStreamEvent {
        return s.StreamEvents
    }
    
    func (s stubSelectObjectContentEventStreamReader) Close() error {
        return nil
    }
    
    func (s stubSelectObjectContentEventStreamReader) Err() error {
        return s.Error
    }
    

    Putting it all together:

    func TestAwsEventStream(t *testing.T) {
        streamEvents := make(chan s3.SelectObjectContentEventStreamEvent, 3)
        eventStreamReader := stubSelectObjectContentEventStreamReader{
            StreamEvents: streamEvents,
            Error:        nil,
        }
    
        mockEventStream := s3.NewSelectObjectContentEventStream(func(o *s3.SelectObjectContentEventStream) {
            o.Reader = eventStreamReader
            o.StreamCloser = io.NopCloser(bytes.NewReader(nil))
        })
    
        mockOutput := &s3.SelectObjectContentOutput{
            EventStream: mockEventStream,
        }
    
        streamEvents <- &s3.RecordsEvent{Payload: []byte("hello world")}
        streamEvents <- &s3.EndEvent{}
        close(streamEvents)
    
        got := awsEventStream(mockOutput)
        want := "hello world"
    
        if got != want {
            t.Errorf("got %v, want %v", got, want)
        }
    }