matryer / moq

Interface mocking tool for go generate
http://bit.ly/meetmoq
MIT License
1.96k stars 126 forks source link

Feature to register multiple mock functions to a method #180

Closed kamatama41 closed 1 year ago

kamatama41 commented 1 year ago

Hi, thank you for developing the great tool!

I would like to a feature mentioned in title. I sometimes want to verify different behaviors of one method in a test case, so I want to register a different function for each call of the method like following.

package publisher

import (
    "context"
    "testing"
)

//go:generate go run github.com/matryer/moq -skip-ensure -pkg publisher -out ./publisher_moq.go . Publisher

type Publisher interface {
    Publish(ctx context.Context, msg string) error
}

func PublishMessage(ctx context.Context, pub Publisher) error {
    if err := pub.Publish(ctx, "hello"); err != nil {
        return err
    }
    if err := pub.Publish(ctx, "world"); err != nil {
        return err
    }
    return nil
}

func TestPublishMessage(t *testing.T) {
    mock := &PublisherMock{}
    // This function will be called at first call of Publish
    mock.RegisterPublishFunc(func(ctx context.Context, msg string) error {
        if msg != "hello" {
            t.Fatalf("unexpected first msg: %v", msg)
        }
        return nil
    })
    // This will be called at second
    mock.RegisterPublishFunc(func(ctx context.Context, msg string) error {
        if msg != "world" {
            t.Fatalf("unexpected second msg: %v", msg)
        }
        return nil
    })
    // Make the third unexpected call an error if possible

    PublishMessage(context.Background(), mock)
}

The test can be written by using current moq like following, but my actual usecase is more complex so that it's so hard to read.

func TestPublishMessage(t *testing.T) {
    mock := &PublisherMock{}
    mock.PublishFunc = func(ctx context.Context, msg string) error {
        switch len(mock.PublishCalls()) {
        case 1:
            if msg != "hello" {
                t.Fatalf("unexpected first msg: %v", msg)
            }
        case 2:
            if msg != "world" {
                t.Fatalf("unexpected second msg: %v", msg)
            }
        default:
            t.Fatalf("unexpected call of Publish")
        }
        return nil
    }

    PublishMessage(context.Background(), mock)
}

How about this idea? Thank you.

breml commented 1 year ago

@kamatama41 Maybe this comment is helpful for you. The basic idea is to have a list of return values for a function in a slice, where each response is returned once. The same thing could be done with a list of functions, that are then pop-ed from the list one after the other.

kamatama41 commented 1 year ago

The mentioned solution seems to be useful for me. I will try it for now 😃