Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to mock variadic function with function argument #1006

Open
BeyondEvil opened this issue Oct 7, 2020 · 4 comments
Open

Unable to mock variadic function with function argument #1006

BeyondEvil opened this issue Oct 7, 2020 · 4 comments

Comments

@BeyondEvil
Copy link

I have a function with this signature:

func (api *Client) PostMessage(channelID string, options ...MsgOption) (string, string, error)`

Where MsgOption is a function-type:

type MsgOption func(*sendConfig) error

The mock:

func (mock *slackAPIMock) PostMessage(channelID string, options ...slack.MsgOption) (string, string, error) {
	args := mock.Called(channelID, options)
	return args.String(0), args.String(1), args.Error(2)
}

Turning it On:

	options := []slack.MsgOption{
		slack.MsgOptionAttachments(expectedAttachment),
		slack.MsgOptionAsUser(true),
	}

	mock.On(
		"PostMessage",
		mock.AnythingOfTypeArgument("string"),
		options,
	).Return(mockedChannel, timestamp, nil)

And the assert:

	mock.AssertCalled(
		t,
		"PostMessage",
		mockedChannel,
		slack.MsgOptionAttachments(expectedAttachment),
		slack.MsgOptionAsUser(true),
	)

Result:

Diff: 1: FAIL:  ([]slack.MsgOption=[0x1376370 0x1376180]) != ([]slack.MsgOption=[0x1376370 0x1376180])

They look the same to me. What am I missing here? 🤔

Full output
panic: 

mock: Unexpected Method Call
-----------------------------

PostMessage(string,[]slack.MsgOption)
		0: "some_channel"
		1: []slack.MsgOption{(slack.MsgOption)(0x1376370), (slack.MsgOption)(0x1376180)}

The closest call I have is: 

PostMessage(mock.AnythingOfTypeArgument,[]slack.MsgOption)
		0: "string"
		1: []slack.MsgOption{(slack.MsgOption)(0x1376370), (slack.MsgOption)(0x1376180)}


Diff: 1: FAIL:  ([]slack.MsgOption=[0x1376370 0x1376180]) != ([]slack.MsgOption=[0x1376370 0x1376180]) [recovered]
	panic: 

mock: Unexpected Method Call
-----------------------------

PostMessage(string,[]slack.MsgOption)
		0: "some_channel"
		1: []slack.MsgOption{(slack.MsgOption)(0x1376370), (slack.MsgOption)(0x1376180)}

The closest call I have is: 

PostMessage(mock.AnythingOfTypeArgument,[]slack.MsgOption)
		0: "string"
		1: []slack.MsgOption{(slack.MsgOption)(0x1376370), (slack.MsgOption)(0x1376180)}


Diff: 1: FAIL:  ([]slack.MsgOption=[0x1376370 0x1376180]) != ([]slack.MsgOption=[0x1376370 0x1376180])
@proclaim
Copy link

proclaim commented Oct 8, 2020

I think this is a gotcha pitfall. Although you see this message: ([]slack.MsgOption=[0x1376370 0x1376180]) != ([]slack.MsgOption=[0x1376370 0x1376180]) and sure from the visual it's the same, but if you think, we're actually comparing 2 different thing, they just have the same value. (you can only compare primative type)

let me explain:
in the postMessage (small p) you accept the message, and you construct the attachment and pass that attachment to PostMessage (cap P), and when you mock the PostMessage it's comparing the created attachment, with the expectedAttachment and although they have the same value, it's different to go

so if you change line 34, from options to mock.Anything it should work.
here's what worked for me (I did remove some irrelevant code)

func TestPostMessage(t *testing.T) {
	setSlackChannel(mockedChannel)

	expectedColor := "good"
	expectedMessage := "test message"
	// expectedAttachment := slack.Attachment{
	// 	Color:    expectedColor,
	// 	Fallback: expectedMessage,
	// 	Text:     expectedMessage,
	// }

	setColor(expectedColor)

	// options := []slack.MsgOption{
	// 	slack.MsgOptionAttachments(expectedAttachment),
	// 	slack.MsgOptionAsUser(true),
	// }
	timestamp := "123456"
	mockAPI := proxySlackAPIMock{}
	mockAPI.On(
		"PostMessage",
		mock.AnythingOfTypeArgument("string"),
		mock.Anything,
	).Return(mockedChannel, timestamp, nil)

	assert.Nil(t, postMessage(&mockAPI, expectedMessage))

	mockAPI.AssertExpectations(t)
	mockAPI.AssertNumberOfCalls(t, "PostMessage", 1)
	// mockAPI.AssertCalled(
	// 	t,
	// 	"PostMessage",
	// 	mockedChannel,
	// 	mock.Anything,
	// 	slack.MsgOptionAsUser(true),
	// )
}

@BeyondEvil
Copy link
Author

BeyondEvil commented Oct 8, 2020

Sure, that will make the test pass @proclaim but it also then doesn't assert that PostMessage was called with the expected attachment. That is, it diminishes the test a bit.

I think the main problem is that the slack.MsgOption* functions returns another function.

@gavwu
Copy link

gavwu commented Oct 12, 2020

the go said:

...
//
// Func values are deeply equal if both are nil; otherwise they are not deeply equal.
...
func DeepEqual(x, y interface{}) bool {

@gavwu
Copy link

gavwu commented Oct 12, 2020

the go said:

...
//
// Func values are deeply equal if both are nil; otherwise they are not deeply equal.
...
func DeepEqual(x, y interface{}) bool {

and mock use the function to test if they are equal

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants