如何 compare/match 模拟中的闭包?
How to compare/match closures in mocks?
TL;DR: 模拟方法接受闭包。我想知道如何创建自定义匹配器 (https://godoc.org/github.com/golang/mock/gomock#Matcher):闭包本身反过来使用私有结构——这意味着我什至无法在我的测试中调用闭包来检查它是否符合预期。
我正在 nlopes/slack (https://github.com/nlopes/slack) 的帮助下使用 Slack API 开发一个小应用程序。
为了测试,我正在用 gomock 模拟 nlopes/slack。为此,我创建了界面
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)
}
我在测试 OpenConversation 或 GetUserByEmail 时没有问题,例如
slackAPIClient.
EXPECT().
GetUserByEmail("some@email.com").
Return(slackUserJohndoe, nil).
Times(1)
当涉及到 PostMessage 时,事情变得更加复杂。在主代码中,调用看起来像
_, _, err := slackAPIClient.PostMessage(channel.ID, slack.MsgOptionText(message, false))
而slack.MsgOptionText(来自nlopes/slack)实际上是返回闭包:
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
}
}
由于方法接受闭包,我需要创建自定义 gomock 匹配器 (https://godoc.org/github.com/golang/mock/gomock#Matcher)。自定义匹配器本身不是问题,它看起来像
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)
}
但是,由于 MsgOptionText 使用 nlopes/slack 私有结构 sendConfig,我想知道如何在我的测试范围内使用它来检查是否符合预期。
我该如何解决这个问题?
牢记
- 在 Golang 中你不能比较函数
- 在这种情况下,我无法通过调用闭包本身来进行间接测试(因为它使用私有第 3 方库的结构作为参数)
我找到的解决方案是模拟 slack.MsgOptionText(message, false),然后 returns 关闭 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)
而且,对于 PostMessage - 正如评论中所建议的那样,我唯一可以检查的是闭包不为零:
slackAPIClient.
EXPECT().
PostMessage("ABCDE", Not(Nil())).
AnyTimes()
TL;DR: 模拟方法接受闭包。我想知道如何创建自定义匹配器 (https://godoc.org/github.com/golang/mock/gomock#Matcher):闭包本身反过来使用私有结构——这意味着我什至无法在我的测试中调用闭包来检查它是否符合预期。
我正在 nlopes/slack (https://github.com/nlopes/slack) 的帮助下使用 Slack API 开发一个小应用程序。
为了测试,我正在用 gomock 模拟 nlopes/slack。为此,我创建了界面
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)
}
我在测试 OpenConversation 或 GetUserByEmail 时没有问题,例如
slackAPIClient.
EXPECT().
GetUserByEmail("some@email.com").
Return(slackUserJohndoe, nil).
Times(1)
当涉及到 PostMessage 时,事情变得更加复杂。在主代码中,调用看起来像
_, _, err := slackAPIClient.PostMessage(channel.ID, slack.MsgOptionText(message, false))
而slack.MsgOptionText(来自nlopes/slack)实际上是返回闭包:
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
}
}
由于方法接受闭包,我需要创建自定义 gomock 匹配器 (https://godoc.org/github.com/golang/mock/gomock#Matcher)。自定义匹配器本身不是问题,它看起来像
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)
}
但是,由于 MsgOptionText 使用 nlopes/slack 私有结构 sendConfig,我想知道如何在我的测试范围内使用它来检查是否符合预期。
我该如何解决这个问题?
牢记
- 在 Golang 中你不能比较函数
- 在这种情况下,我无法通过调用闭包本身来进行间接测试(因为它使用私有第 3 方库的结构作为参数)
我找到的解决方案是模拟 slack.MsgOptionText(message, false),然后 returns 关闭 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)
而且,对于 PostMessage - 正如评论中所建议的那样,我唯一可以检查的是闭包不为零:
slackAPIClient.
EXPECT().
PostMessage("ABCDE", Not(Nil())).
AnyTimes()