Mocking a inner function(or even more deep) for testing in go

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

Mocking a inner function(or even more deep) for testing in go

问题

有没有办法实现这个?我想在一个函数调用内部模拟另一个函数调用的响应。示例代码如下:

main.go

type Crs struct {}

func (cr Crs) TheFunction() error {
   // Some code
   _ := ToMockResponse(hello string)
   return nil
}

func ToMockResponse() error {
    return somerror
}

在我的测试文件中:

main_test.go

func TestTheFunction(t *testing.T) {
    cr := Crs{}
    mockInstance := new(randomMock)
    mockInstance.On("ToMockResponse").Return(nil)

    err := cr.TheFunction()
    assert.NoError(t, err)
}

我不确定我所做的是否正确。我想要实现的是,每当在TheFunction内部调用ToMockResponse时,我只想要一个模拟响应。

我已经看过并阅读了大部分的教程,但它们都只展示了对类的方法进行模拟响应的示例。但是没有函数对函数的模拟示例。希望你能提供一个解决方案。

英文:

is there a way to achieve this? I want to Mock the response of a function call from within another function call. Example

main.go

type Crs struct {}
func (cr crs)TheFunction() error {
   // Some code
   _ := ToMockResponse(hello string)
   return nil
}

func ToMockResponse() error {
    return somerror
}

and in my test file

main_test.go

func TestTheFunction(t *testing.T) {
    cr = Crs{}
    mockInstance = new(randomMock)
    mockInstance.On("ToMockResponse").Return(nil)

    err := cr.TheFunction()
    assert.NoError(t, err)
}

I'm not sure if what I did is correct. What I'm trying to achieve is that I just want to have a Mock response of ToMockResponse whenever it's being called inside TheFunction

I have seen and read most of the tutorial but all of them shows a Mock response from a method of a class. But there are no function-to-function mocking samples. Hope you can provide a solution to this.

答案1

得分: 1

不讨论是否应该这样做,并假设你有充分的理由这样做,一种模拟函数的方法是通过间接调用它们。然后,你可以在测试的目的下替换适当的替代方案。

接口是实现这一目标的一种方式,但是Go语言支持将函数作为值,所以另一种方式是使用适当函数类型的简单变量:

根据你的示例进行调整,可能如下所示:

var toMockResponse = ToMockResponse

func (cr crs)TheFunction() error {
   // Some code
   _ := toMockResponse(hello string)
   return nil
}

func ToMockResponse() error {
    return somerror
}

以及你的测试:

func TestTheFunction(t *testing.T) {
    cr = Crs{}
    ofn := toMockResponse
    toMockResponse = func(string) error { return nil }
    defer func() { toMockResponse = ofn }()

    err := cr.TheFunction()
    assert.NoError(t, err)
}

我假设你需要导出ToMockResponse;函数变量故意导出,以便它不能在包外部被操纵。

如果被重定向的函数本身没有被导出,拒绝使用导出与非导出符号的大小写区分来区分它们的能力,那么你可能需要在命名上更有创意。例如,一个名为toMockResponseFn的变量。

脚注

ofn是"original function"的记忆方法。在更复杂的测试中,你可以通过为每个所需的ofn创建一个作用域来保留这种模式(defer相对于包含的函数而不是作用域操作):

   {
      ofn := toMockResponse
      toMockResponse = func(string) error { return nil }
      defer func() { toMockResponse = ofn }()
   }
   {
      ofn := now
      now = func() time.Time { return time.Date(..etc..) }
      defer func() { now = ofn }()
   }

英文:

Without getting into whether you should do this, and assuming that you have a valid reason to do so, one way to mock functions is to use them via a level of indirection. You can then substitute a suitable alternative in your indirection for the purposes of testing.

An interface is one way to achieve that, but GoLang supports functions as values, so another way is to use a simple variable of an appropriate function type:

Adapted to your example, this might look similar to:

var toMockResponse = ToMockResponse

func (cr crs)TheFunction() error {
   // Some code
   _ := toMockResponse(hello string)
   return nil
}

func ToMockResponse() error {
    return somerror
}

and your test:

func TestTheFunction(t *testing.T) {
    cr = Crs{}
    ofn := toMockResponse
    toMockResponse = func(string) error { return nil }
    defer func() { toMockResponse = ofn }()

    err := cr.TheFunction()
    assert.NoError(t, err)
}

I have assumed that you need to export the ToMockResponse; the function variable is deliberately not exported so that it cannot be manipulated outside of the package itself.

If the function being redirected is itself not exported, denying the ability to use the case difference of exported vs non-exported symbols to differentiate them, then you may need to be a bit more creative with your naming. e.g. a variable called toMockResponseFn.

Footnote

ofn is mnemonic for "original function". In a more complicated test you can preserve the pattern by creating a scope for each ofn you need (defer operates w.r.t the containing function, not the scope):

   {
      ofn := toMockResponse
      toMockResponse = func(string) error { return nil }
      defer func() { toMockResponse = ofn }()
   }
   {
      ofn := now
      now = func() time.Time { return time.Date(..etc..) }
      defer func() { now = ofn }()
   }

huangapple
  • 本文由 发表于 2022年11月27日 19:42:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/74589714.html
匿名

发表评论

匿名网友

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

确定