【问题标题】:Why we use interface for mocking methods Golang为什么我们使用接口来模拟 Golang 方法
【发布时间】:2020-12-02 07:12:01
【问题描述】:

我是 Golang 新手,一直在探索,但不清楚单元测试中的 mocking。谁能解释以下具体问题?

问题1:在 Golang 中编写单元测试,为什么我们需要有接口来模拟方法,为什么不只是 struct ?

问题2:为什么我们在struct中注入接口(我们在其中调用外部方法)

带结构-

type GlobalData struct {}

var (
    GlobalObj = GlobalData{}
)

func (g GlobalData) GetGlobalData(a string) string{
    return a
}

有接口定义-

type GlobalInterface interface {
    GetGlobalData(a string) string
}

type GlobalData struct {}

var (
    GlobalObj = GlobalData{}
)

func (g GlobalData) GetGlobalData(a string) string{
    return a
}

谢谢

【问题讨论】:

  • 很大一部分单元测试可以(并且已经)在不引入接口和模拟的情况下完成。我不知道你有没有得到这些信息。查看标准库中的单元测试,了解 Go 中的实际最佳测试实践。
  • 如果你想使用模拟,你必须使用几乎所有语言的界面。接口是您可以在测试中用模拟或生产中的真实实现替代的类型。这是嘲笑的基本品质。
  • @Volker,谢谢,您能提供任何参考吗?因为我的探索使我使用接口来模拟方法。
  • @Adrian,感谢您提供信息。你能解释一下它背后的逻辑吗,我们不能用struct来模拟吗?
  • 结构是一种实现。为了能够交替使用两种不同的实现(模拟 a 和真实实现),您必须使用接口。

标签: unit-testing go struct interface mocking


【解决方案1】:

如果你有关于包用户类型的方法,比如说,例如。 打包用户

type User struct {
 name string
}

func (u *User) GetUserProfile() UserProfile{}

现在在目录包中导入:

package catalog

import user

func getUserCatalog(user user.User) []catalog {
 user.GetUserProfile()
}

现在测试 getUserCatalog 方法有两种方法:

1. var getUserProfileFunc = user.GetUserProfile

使用这种方法可以在测试运行时轻松通过模拟,例如:

getUserProfile = func() UserProfile { 
 return fakeUserProfile 
}

这是最简单的测试方法。

现在还有另一种使用界面的方式,在包用户中添加一个类似的界面

type UserInterface interface {
  GetUserProfile() UserProfile
}

如果用户包是您无法控制的库,则创建您自己的界面,键入并使用它。

在这种情况下,目录包中的测试将变为:

因为现在方法将从 UserInterface 类型而不是从 UserType 调用,因此在测试时:

UserInterface = fakeUserStruct

并按照以下步骤操作

//1. define type of func to return 

type typeGetUserProfile func() UserProfile

//2. create a var to return

var mockedGetUserProfile typeGetUserProfile

//3. create a type

type FakeUser struct{}

//4. implement method interface

func (user *FakeUserStruct) GetUserProfile() UserProfile{
  return mockedGetUserProfile
 }

现在运行测试时:

mockerGetUserProfile = func() UserProfile {
  return fakeUserProfile
 }

有一个模拟库可以帮助创建用于模拟的样板代码。检查这个https://github.com/stretchr/testify

还有很多其他的mock库,但是我用过这个,真的很酷。

我希望这会有所帮助。

如果不是请告诉我,我会给出一些示例代码并将其推送到 Github。

另外请查看https://levelup.gitconnected.com/utilizing-the-power-of-interfaces-when-mocking-and-testing-external-apis-in-golang-1178b0db5a32

【讨论】:

【解决方案2】:

问题一:在 Golang 中编写单元测试,为什么我们需要有接口来模拟方法,为什么不只是 struct ?

回答:不是强制性的

问题2:为什么要在struct中注入接口(这里我们调用外部方法)

回答:因为,它可以帮助您替换实际的函数调用(作为单元测试的一部分,这可能会触发一些超出范围的操作,例如 数据库调用一些 API 调用 等)通过注入MockStruct(这将实现与实际代码中相同的interface)。 多态性用简单的话来说。

因此,您创建一个MockStruct 并为其定义自己的mockMethods。作为多态性,您的单元测试选择MockStruct 没有抱怨。调用实际 DB 或 http 端点不属于单元测试

仅供参考,我可以将您指向我的一个 github 代码库,我在其中为 a file 编写了 small test case。如您所见,我嘲笑了:

  1. GuestCartHandler interface,这让我不能打电话给actual implementation
  2. 使用"github.com/DATA-DOG/go-sqlmock" 包模拟sql connection。这帮助我避免建立实际的db client(因此,在单元测试时不依赖数据库)

如果您从概念上理解了这个想法,或者您需要更多说明,请告诉我。

【讨论】:

  • 嘿@Shashank,感谢您的回答和代码的良好参考/结构。这里我看到的,如果有错误请纠正我 1.处理程序包依赖于“dao”包。我们已经从处理程序包的“guestCartItemsImpl”结构中注入了“GuestCartHandler”接口。注入的主要目的是模拟 dao 包中的方法,同时编写单元测试 fir 处理程序包的方法! 2.但是为什么要实现这个接口呢?如果必须采用这种方式,那么我只想了解其背后的逻辑。在我看到带有单元测试接口的示例的每个地方。
  • @SiyaramMalav:您是否按照我的回答中的建议阅读了多态性? “通过创建接口,您可以在actualmock 函数之间进行切换”。接口可帮助您定义 SomeType ,并且一旦实现这些方法,mock 就会变成 SomeType。因此,当您在单元测试中交换代码时,代码不会抱怨。请阅读这行双引号,如果有帮助,请告诉我
  • 嗨@Shashank,我无法将多态性与这种情况联系起来,因为多态性谈到了具有相同方法名称但参数/参数类型数量不同的方法的不同实现。但是这里的参数在实际代码和单元测试中是一样的。
  • @SiyaramMalav:In computer science, it describes the concept that objects of different types can be accessed through the same interface.。取自stackify.com/oop-concept-polymorphism。由于 Go 不支持继承,也许stackoverflow.com/questions/38123911/golang-method-override 也可以帮助您理解所提到的上下文。请记住,唯一的原因是 replace actual method with mock ones, without go compiler complaining about it 。这有帮助吗?我在最初的日子里也很挣扎,所以我能理解你的情况:)
  • @ShshankVivek,是的,谢谢 :) 我必须问这些,因为你似乎更熟悉这些 go 的东西。 1. 我在看这个-github.com/shashankvivek/e-food-server/blob/… 如果我为“Validate”方法编写单元测试,那么是使用接口方式来模拟“validateItems”和“validateOfferItems”等方法吗?或任何其他最好的方法!因为我有一个“基本”包(有多个文件)中的用例,我们必须模拟这些方法。
猜你喜欢
  • 2011-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-26
  • 2010-10-13
  • 2020-12-08
  • 1970-01-01
相关资源
最近更新 更多