vendredi 23 mars 2018

Testing Emitter func with channel return value in go

I am having a hard time getting my test for my emitter function which passes results through a channel for a data pipeline. This function will be triggered periodically and will pull records from the database. I compiled an stripped done version for this question the real code would more complex but would follow the same pattern. For testing I mocked the access to the database because I want to test the behavoir of the Emitter function.

I guess code is more than words:

This is the method I want to test:

//EmittRecord pull record from database
func EmittRecord(svc Service, count int) <-chan *Result {
    out := make(chan *Result)

    go func() {
        defer close(out)
        for i := 0; i < count; i++ {
            r, err := svc.Next()
            if err != nil {
                out <- &Result{Error: err}
                continue
            }
            out <- &Result{Payload: &Payload{
                Field1: r.Field1,
                Field2: r.Field2,
            }, Error: nil}
        }

    }()

    return out
}

I have a couple of types with an interface:

//Record is a Record from db
type Record struct {
    Field1 string
    Field2 string
}

//Payload is a record for the data pipeline
type Payload struct {
    Field1 string
    Field2 string
}

//Result is a type for the data pipeline
type Result struct {
    Payload *Payload
    Error   error
}

//Service is an abstraction to access the database
type Service interface {
    Next() (*Record, error)
}

This is my service Mock for testing:

//MockService is a struct to support testing for mocking the database
type MockService struct {
    NextMock func() (*Record, error)
}

//Next is an Implementation of the Service interface for the mock
func (m *MockService) Next() (*Record, error) {
    if m.NextMock != nil {
        return m.NextMock()
    }
    panic("Please set NextMock!")
}

And finally this is my test method which does not work. It does not hit the done case and das not hit the 1*time.Second timeout case either ... the test just times out. I guess I am missing something here.

 func TestEmitter(t *testing.T) {

    tt := []struct {
        name           string
        svc            runner.Service
        expectedResult runner.Result
    }{

        {name: "Database returns error",
            svc: &runner.MockService{
                NextMock: func() (*runner.Record, error) {
                    return nil, fmt.Errorf("YIKES")
                },
            },
            expectedResult: runner.Result{Payload: nil, Error: fmt.Errorf("RRRR")},
        },
        {name: "Database returns record",
            svc: &runner.MockService{
                NextMock: func() (*runner.Record, error) {
                    return &runner.Record{
                        Field1: "hello",
                        Field2: "world",
                    }, nil
                },
            },
        },
    }

    for _, tc := range tt {

        t.Run(tc.name, func(t *testing.T) {
            done := make(chan bool)
            defer close(done)

            var output <-chan *runner.Result
            go func() {
                output = runner.EmittRecord(tc.svc, 1)
                done <- true
            }()
            found := <-output
            <-done
            select {
            case <-done:
            case <-time.After(1 * time.Second):
                panic("timeout")
            }

            if found.Error.Error() != "Hello" {
                t.Errorf("FAIL: %s, expected: %s; but got %s", tc.name, tc.expectedResult.Error.Error(), found.Error.Error())

            } else if reflect.DeepEqual(found.Payload, tc.expectedResult.Payload) {
                t.Errorf("FAIL: %s, expected: %+v; got %+v", tc.name, tc.expectedResult.Payload, found.Payload)
            }

        })
    }

}

It would be great, if someone could give me an advice what I missing here and maybe some input how to verify the count of the EmittRecord function right now it is only set to 1 Thanks in advance

Aucun commentaire:

Enregistrer un commentaire