TL;DR: mocked method accepts closure. I wonder how to create custom matcher (https://godoc.org/github.com/golang/mock/gomock#Matcher): closure itself in turn is working with private structure - meaning I can't even call the closure in my test to check it against expectations.
I'm working on a small app using Slack API with help of nlopes/slack (https://github.com/nlopes/slack).
For testing, I'm mocking nlopes/slack with gomock. For that I've created interface
type slackAPI interface {
OpenConversation(*slack.OpenConversationParameters) (*slack.Channel, bool, bool, error)
PostMessage(channelID string, options ...slack.MsgOption) (string, string, error)
GetUserByEmail(email string) (*slack.User, error)
}
I have no problem testing OpenConversation or GetUserByEmail, e.g.
slackAPIClient.
EXPECT().
GetUserByEmail("some@email.com").
Return(slackUserJohndoe, nil).
Times(1)
Things get more complicated when it comes to PostMessage. In main code the call looks like
_, _, err := slackAPIClient.PostMessage(channel.ID, slack.MsgOptionText(message, false))
And slack.MsgOptionText (from nlopes/slack) is actually returning closure:
func MsgOptionText(text string, escape bool) MsgOption {
return func(config *sendConfig) error {
if escape {
text = slackutilsx.EscapeMessage(text)
}
config.values.Add("text", text)
return nil
}
}
Since method is accepting closure, I need to create custom gomock matcher (https://godoc.org/github.com/golang/mock/gomock#Matcher). Custom matcher itself is not a problem, it would look something like
type higherOrderFunctionEqMatcher struct {
x interface{}
}
func (e hofEqMatcher) Matches(x interface{}) bool {
//return m.x == x
return true
}
func (e hofEqMatcher) String(x interface{}) string {
return fmt.Sprintf("is equal %v", e.x)
}
However, since MsgOptionText uses nlopes/slack private structure sendConfig, I wonder how can I even work with that in scope of my test to check equality to expectations.
How should I tackle such problem?
Bearing in mind that
the solution I've found is to mock slack.MsgOptionText(message, false), which in turn returns closure for PostMessage(channelID string, options ...slack.MsgOption):
type slackMsgCreator interface {
MsgOptionText(string, bool) slack.MsgOption
}
type slackMsgCreatorInst struct{}
func (s slackMsgCreatorInst) MsgOptionText(text string, escape bool) slack.MsgOption {
return slack.MsgOptionText(text, escape)
}
...
slackMsgCreator.
EXPECT().
MsgOptionText("Dear John Doe, message goes here", false).
Return(slack.MsgOptionText("Dear John Doe, message goes here", false)).
Times(1)
And, as for PostMessage - as was advised in comments, the only thing that I could check is that closure is not nil:
slackAPIClient.
EXPECT().
PostMessage("ABCDE", Not(Nil())).
AnyTimes()