模拟一个类型,其中接口签名的类型也被模拟了。

huangapple go评论90阅读模式
英文:

Mocking a type where interface signature's types are also mocked

问题

我正在尝试在不编辑现有库的情况下编写一个包装器,同时允许使用模拟进行测试。

为了进行测试和模拟,我创建了满足现有库类型签名的接口,并使用了mockgen。我还必须模拟这些接口类型签名中函数返回的一些类型。

然而,这样做会导致接口不再满足现有库的要求。

示例:

我有一个ClientType,它有一个名为Subscribe的函数,返回一个SubscriptionType

func NewClientType() ClientType {
	return ClientType{"project name"}
}

type ClientType struct {
	projectID string
}

func (l *ClientType) Subscribe(subID string) SubscriptionType {
	return SubscriptionType{subID}
}

type SubscriptionType struct {
	subscriptionID string
}

func (t *SubscriptionType) Receive() {
	fmt.Println("Stuff")
}

我想模拟Receive方法。

这将允许我创建一个Client,将其订阅到一个主题,然后在测试中模拟接收消息。

我尝试了以下代码:

type MyLibraryWrapper struct {
	ClientType
}

type MockedClientType interface {
	Subscribe(string) MockedSubscriptionType
}

type MockedSubscriptionType interface {
	Receive()
}

func main() {
	client := NewClientType()

	myMockedType := NewWrapper(client)
	sub := myMockedType.Subscribe("a")
	_ = sub // 使用它
}

func NewWrapper(client ClientType) MyLibraryWrapper {
	return MyLibraryWrapper{client}
}

这会产生以下错误:

cannot use client (type ClientType) as type MockedClientType in argument to NewWrapper:
	ClientType does not implement MockedClientType (wrong type for Subscribe method)
		have Subscribe(string) SubscriptionType
		want Subscribe(string) MockedSubscriptionType

演示链接:

https://go.dev/play/p/6Pr_Y4VtOAW

英文:

I'm trying to write a wrapper around an existing library that I do not want to edit, while allowing testing via mocking.

To allow for testing and mocking, I have created interfaces that satisfy the type signatures of the existing library and am using mockgen. I must also mock some of the types returned by the functions in these interface type signatures.

However, doing this causes the interface to no longer satisfy the existing library.

Example:

I have a ClientType, this has a function Subscribe, which returns a SubscriptionType.

func NewClientType() ClientType {
	return ClientType{"project name"}
}

type ClientType struct {
	projectID string
}

func (l *ClientType) Subscribe(subID string) SubscriptionType {
	return SubscriptionType{subID}
}

type SubscriptionType struct {
	subscriptionID string
}

func (t *SubscriptionType) Receive() {
	fmt.Println("Stuff")
}

I want to mock the Receive method.

This will allow me to create a Client, Subscribe it to a topic, and then mock this Receiving a message in testing.

I tried:

type MyLibraryWrapper struct {
	ClientType
}

type MockedClientType interface {
	Subscribe(string) MockedSubscriptionType
}

type MockedSubscriptionType interface {
	Receive()
}

func main() {
	client := NewClientType()

	myMockedType := NewWrapper(client)
	sub := myMockedType.Subscribe("a")
	_ = sub // use it
}

func NewWrapper(client ClientType) MyLibraryWrapper {
	return MyLibraryWrapper{client}
}

That gives:

<!-- language: lang-none -->

cannot use client (type ClientType) as type MockedClientType in argument to NewWrapper:
	ClientType does not implement MockedClientType (wrong type for Subscribe method)
		have Subscribe(string) SubscriptionType
		want Subscribe(string) MockedSubscriptionType

Live demo:

https://go.dev/play/p/6Pr_Y4VtOAW

答案1

得分: 1

你的库API是硬编码为返回struct。在模拟任何东西之前,你必须先将每个struct包装在一个接口中。

通过嵌入,这个任务变得更容易。

type MyLibraryWrapper struct {
    ClientType
}

type MyClientType interface {
    Subscribe(string) MySubscriptionType
}

type MySubscriptionType interface {
    Receive()
}

func NewWrapper(client ClientType) MyClientType {
    return MyLibraryWrapper{client}
}

现在你可以进行模拟了...

type MockLibraryWrapper struct {}

type MockSubscriptionType struct {}

func (MyMockLibraryWrapper) Subscribe(string) MySubscriptionType {
    return MockSubscriptionType{}
}

func (MockSubscriptionType) Receive() {}

func NewMockClientType() MyClientType {
    return MockLibraryWrapper{}
}

示例用法:

func main() {
    realClient := NewWrapper(NewClientType())
    sub := realClient.Subscribe("a")
    sub.Receive()

    mockClient := NewMockClientType()
    sub := mockClient.Subscribe("a")
    sub.Receive()
}
英文:

Your library API is hardcoded to return structs. You have to wrap each of its struct in an interface first before you can mock anything.

With embedding that task becomes easier.

type MyLibraryWrapper struct {
	ClientType
}

type MyClientType interface {
	Subscribe(string) MySubscriptionType
}

type MySubscriptionType interface {
	Receive()
}

func NewWrapper(client ClientType) MyClientType {
	return MyLibraryWrapper{client}
}

Now you can mock it...

type MockLibraryWrapper struct {}

type MockSubscriptionType struct {}

func (MyMockLibraryWrapper) Subscribe(string) MySubscriptionType {
	return MockSubscriptionType{}
}

func (MockSubscriptionType) Receive() {}

func NewMockClientType() MyClientType {
	return MockLibraryWrapper{}
}

Example usage:

func main() {
	realClient := NewWrapper(NewClientType())
	sub := realClient.Subscribe(&quot;a&quot;)
	sub.Receive()

	mockClient := NewMockClientType()
	sub := mockClient.Subscribe(&quot;a&quot;)
	sub.Receive()
}

huangapple
  • 本文由 发表于 2021年12月1日 23:45:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/70187195.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定