unit-testingtestinggogomock

Testing with Gomock returns error: Expected call has already been called the max number of times


I am using Gomock https://godoc.org/github.com/golang/mock and mockgen

The Source code for this test is:

package sqs

import (
    "fmt"
    "log"
    "os"
    "runtime"

    "github.com/aws/aws-sdk-go/aws/session"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/service/sqs"
    "github.com/aws/aws-sdk-go/service/sqs/sqsiface"
)

var sess *session.Session
var svc *sqs.SQS
var queueURL string

func init() {
    // Setting the runtime to run with max CPUs available
    runtime.GOMAXPROCS(runtime.NumCPU())

    sess = session.Must(session.NewSessionWithOptions(session.Options{
        SharedConfigState: session.SharedConfigEnable,
    }))

    svc = sqs.New(sess)

    queueURL = os.Getenv("QUEUE_URL")
}

type Poller interface {
    Poll(chan bool)
}

// NewPoller is a factory to create a Poller object
func NewPoller(msgr Messenger) Poller {
    p := &poller{
        m: msgr,
    }
    return p
}

type poller struct {
    m Messenger
}

func (p *poller) Poll(done chan bool) {
    sqsMsgCh := make(chan *sqs.Message, 100)
    for {
        messages, err := p.m.GetMessage()
        if err != nil {
            log.Printf("error when getting message")
            if len(messages) == 0 {
                // Stop the system
                log.Printf("I am here")
                done <- true
            }
        }
        for _, msg := range messages {
            sqsMsgCh <- msg
        }
    }
}

type Messenger interface {
    GetMessage() ([]*sqs.Message, error)
}

func NewMessenger() Messenger {
    return &messenger{
        s: svc,
    }
}

type messenger struct {
    s sqsiface.SQSAPI
}

func (m *messenger) GetMessage() ([]*sqs.Message, error) {
    result, err := m.s.ReceiveMessage(&sqs.ReceiveMessageInput{
        AttributeNames: []*string{
            aws.String(sqs.MessageSystemAttributeNameSentTimestamp),
        },
        MessageAttributeNames: []*string{
            aws.String(sqs.QueueAttributeNameAll),
        },
        QueueUrl:            aws.String(queueURL),
        MaxNumberOfMessages: aws.Int64(10),
        VisibilityTimeout:   aws.Int64(36000), // 10 hours
        WaitTimeSeconds:     aws.Int64(0),
    })

    if err != nil {
        fmt.Println("Error", err)
        return nil, err
    }

    msgs := result.Messages
    if len(msgs) == 0 {
        fmt.Println("Received no messages")
        return msgs, err
    }
    return msgs, nil
}

The test case for this Source file is here:

package sqs

import (
    "errors"
    "testing"

    "path_to_the_mocks_package/mocks"
    "github.com/golang/mock/gomock"

    "github.com/aws/aws-sdk-go/service/sqs"
)


func TestPollWhenNoMessageOnQueue(t *testing.T) {
    mockCtrl := gomock.NewController(t)
    defer mockCtrl.Finish()

    msgr := mocks.NewMockMessenger(mockCtrl)
    mq := make([]*sqs.Message, 1)
    err := errors.New("Mock Error")
    // msgr.EXPECT().GetMessage().Return(mq, err) //.Times(1)
    // msgr.GetMessage().Return(mq, err) //.Times(1)
    msgr.EXPECT().GetMessage().Return(mq, err)

    p := NewPoller(msgr)
    done := make(chan bool)
    go p.Poll(done)
    <-done
    t.Logf("Successfully done: %v", done)
}

When I run the tests I am getting the following error:

sqs\controller.go:150: Unexpected call to *mocks.MockMessenger.GetMessage([]) at path_to_mocks_package/mocks/mock_messenger.go:38 because: Expected call at path_to_sqs_package/sqs/sqs_test.go:35 has already been called the max number of times. FAIL

If I write my own mock as follows the test case executes successfully:

type mockMessenger struct {
    mock.Mock
}

func (m *mockMessenger) GetMessage() ([]*sqs.Message, error) {
    msgs := make([]*sqs.Message, 0)
    err := errors.New("Error")
    return msgs, err
}

Solution

  • You are implicitly telling gomock that you only expect a single call.

    msgr.EXPECT().GetMessage().Return(mq, err)
    

    Adding a number of Times to the mock, allows you to return those values more than once.

    msgr.EXPECT().GetMessage().Return(mq, err).AnyTimes()
    

    For more details please read the gomock's AnyTimes documentation.