4

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)

}
LoganPaul
  • 59
  • 11
  • The function under test uses `EventStream` as a field, but the mocked struct declares it as a method. What exactly are you trying to do? Also, you are passing the original object, not the mocked object to the function under test. – Burak Serdar Jun 17 '23 at 15:14
  • @BurakSerdar can you help with the code to mock `func awsEventStream(resp *s3.SelectObjectContentOutput) string {}` as i am not able to do so. I am getting struck and not able to proceed ahead. – LoganPaul Jun 19 '23 at 07:29

2 Answers2

4

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)
    }
}
wijayaerick
  • 703
  • 6
  • 12
2

The answer given by @wijayaerick is appropriate and should cover around 60% of the code. But in case OP wants to cover the test case to 100% then he needs to traverse all the switch case statements. Below is my code that I have tried and it works as needed.

Few things that you would need to change is:

Make channel size to 3 streamEvents := make(chan s3.SelectObjectContentEventStreamEvent, 3)

Add RecordsEvent, EndEvent and StatsEvent to streamEvents as below:

statsEvent := &s3.StatsEvent{
    Details: &s3.Stats{
        BytesScanned:   aws.Int64(1024),
        BytesProcessed: aws.Int64(2048),
    },
}
streamEvents <- &s3.RecordsEvent{Payload: []byte("hello world")}
streamEvents <- &s3.EndEvent{}
streamEvents <- statsEvent

Your overall code will look like below:

package main

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

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
}

func TestAwsEventStream(t *testing.T) {

    type testCase struct {
        arg1 s3.SelectObjectContentEventStreamEvent
        want s3.SelectObjectContentEventStreamEvent
    }
    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,
    }
    statsEvent := &s3.StatsEvent{
        Details: &s3.Stats{
            BytesScanned:   aws.Int64(1024),
            BytesProcessed: aws.Int64(2048),
        },
    }
    streamEvents <- &s3.RecordsEvent{Payload: []byte("hello world")}
    streamEvents <- &s3.EndEvent{}
    streamEvents <- statsEvent
    close(streamEvents)

    got := awsEventStream(mockOutput)
    want := "hello world"

    if got != want {
        t.Errorf("got %v, want %v", got, want)
    }
}

enter image description here

Vishwa Ratna
  • 5,567
  • 5
  • 33
  • 55