简体   繁体   English

Go中的模拟接收器功能

[英]Mocking receiver functions in Go

I'm trying to unit test a receiver function that calls other receiver functions in that struct. 我正在尝试对在该结构中调用其他接收器函数的接收器函数进行单元测试。

Let's say I want to test Three() and mock the call to two() in the following: 假设我要测试Three()并在下面模拟对two()的调用:

type MyStruct struct {
    a string
    b string
}

func (m *MyStruct) one() int {
    return 2
}

func (m *MyStruct) two() int {
    return m.one() * 2
}

func (m *MyStruct) Three() int {
    return m.two() * 2
}

I was following method two of the following answer . 我正在遵循以下答案中的方法二。

I created a custom constructor for every single function that I wanted to unit test and overrode those methods with mocked versions. 我为要进行单元测试的每个单个函数创建了一个自定义构造函数,并使用模拟版本覆盖了这些方法。 But I thought it may not be easy to maintain the code once the number of functions grow. 但是我认为一旦函数数量增加,维护代码可能并不容易。

Is there any preferred way of mocking such functions? 有没有模拟这些功能的首选方法? I wish the official documentation had some guidelines on how to mock things in different scenarios, similar to what mox on Python provides. 我希望官方文档对如何在不同情况下模拟事物提供一些指导,类似于Python上的mox。

Also, note that I don't want to use a third party mocking library. 另外,请注意,我不想使用第三方模拟库。

That is a really un-idiomatic way to test your stuff. 这是测试您的东西的一种真正不习惯的方法。 All this mocking might be needed in other languages, but please don't do it in Go. 其他语言可能需要所有这些模拟,但是请不要在Go中进行。

The natural way to test your code in the example you gave would be: 1) Write a table driven test for MyStruct.one and make sure you test all cases. 在给出的示例中测试代码的自然方法是:1)为MyStruct.one编写表驱动测试,并确保测试所有案例。 Now that you know one works perfectly fine 2) do the same with MyStruct.two . 现在您知道one可以很好地工作2)对MyStruct.two进行相同的MyStruct.two Note that testing unexported stuff is possible, useful and common in Go. 请注意,在Go中测试未导出的内容是可能的,有用的并且是常见的。 Now there is no longer a need need to mock some methods, just 3) write some table driven test for MyStruct.Three and check it works. 现在不再需要模拟某些方法,只需3)为MyStruct.Three写一些表驱动的测试。 MyStruct.Three并检查它是否有效。

But maybe your methods one and two do fancier stuff, and access the environment (filesystem, database, network) and you do not want your tests of Three to depend on that? 但是,也许您的方法one和方法two做得更好,并且可以访问环境(文件系统,数据库,网络),而您不希望对方法Three的测试依赖于此吗? So refactor your code! 因此,重构您的代码! Maybe Three should not be a method of MyStruct but a function which takes an interface OneAndTwoer as an argument and your production code calls Three with "real" MyStructs while your testcode calls it with InMemoryMyStrcuts which do not depend on the environment? 也许Three不应该是MyStruct的方法,而是一个以interface OneAndTwoer作为参数并且您的生产代码使用“真实的” MyStructs调用Three的函数,而您的测试代码使用不依赖于环境的InMemoryMyStrcuts调用它的函数? You could call it a mock, I'd call it a different implementation of an interface. 您可以称其为模拟,我称其为接口的另一种实现。

In your example it is simple to give advice: Use table driven tests for one , two and Three and do not mock. 在您的示例中,很容易给出建议:对表one ,表two和表Three使用表驱动测试,并且不要模拟。 For a more realistic problem the advice might be different but it is hard to give a general advice without knowing the circumstances. 对于更现实的问题,建议可能有所不同,但是在不了解具体情况的情况下很难给出一般性建议。 Best general advice is: Take a look at the test in the standard library where you'll find useful patterns for almost every testing scenario. 最好的一般建议是:看一下标准库中的测试,您将在其中找到几乎每种测试方案都有用的模式。

You can do a refactor to pass two as a func in Three , so that you can directly mock the input. 您可以进行重构以将2作为函数传递给Three ,以便您可以直接模拟输入。

func main() {
    var ms MyStruct
    fmt.Println(ms.one())
    fmt.Println(ms.two())
    fmt.Println(ms.Three(ms.two))
}

type MyStruct struct {}

func (m *MyStruct) one() int {
    return 2
}

func (m *MyStruct) two() int {
    return m.one() * 2
}

func (m *MyStruct) Three(two func() int) int {
    return two() * 2
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM