GoMock中的可选调用

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

Optional calls in GoMock

问题

我想使用GoMock来测试一些代码,但不想让我的测试与被测试的实际实现过于紧密地耦合。但是,GoMock似乎要求我能够预先确定被测试代码将进行的准确调用,即使这不是我试图测试的合同的一部分。有没有办法解决这个问题?

例如,假设我正在测试一些验证逻辑,其中被测试的代码需要接受一个名为mockedObjectWidget,如果mockedObject.CheckConditionA()mockedObject.CheckCondition1()返回false,则返回false,否则返回true。一个正确的实现是:

func UnderTest(mockedObject *Widget) bool {
    if !mockedObject.CheckConditionA() {
        return false
    }
    return mockedObject.CheckCondition1()
}

另一个实现是:

func UnderTest(mockedObject *Widget) bool {
    if !mockedObject.CheckCondition1() {
        return false
    }
    return mockedObject.CheckConditionA()
}

第三个实现是:

func UnderTest(mockedObject *Widget) bool {
    cA := mockedObject.CheckConditionA()
    c1 := mockedObject.CheckCondition1()
    return cA && c1
}

第四个实现是:

func UnderTest(mockedObject *Widget) bool {
    if TodayIsAnAlternateTuesday() {
        return mockedObject.CheckCondition1() && mockedObject.CheckConditionA()
    }
    return mockedObject.CheckConditionA() && mockedObject.CheckCondition1()
}

我的测试不应该关心使用了这些实现中的哪一个;我只想检查是否满足了合同。

在这样的测试中,我该如何设置模拟对象?被测试的代码允许但不要求调用某个方法。

对于CheckConditionA()返回false的情况的测试,我需要设置类似mockedObject.EXPECT().CheckConditionA().Return(false)的东西。但是,其中一些实现还需要mockedObject.EXPECT().CheckCondition1().Return(true)才能正常工作,否则我的测试将因为意外调用而失败。另一方面,如果我提供了这个EXPECT,有些实现将会失败,因为缺少调用。

GoMock是否支持编写这种类型的测试?如果不支持,还有什么其他工具可以让我在不构建真实的Widget的情况下测试这个函数?

英文:

I want to use GoMock to test some code, without coupling my tests too tightly to the actual implementation of the thing under test. But GoMock seems to require that I be able to say in advance exactly what calls the code under test will make, even if that's not part of the contract I'm trying to test. Is there a way around this?

For example, say I'm testing some validation logic, where the code under test is required to take a Widget named mockedObject, and return false if either mockedObject.CheckConditionA() or mockedObject.CheckCondition1() returns false, and to return true otherwise. One correct implementation is:

func UnderTest(mockedObject *Widget) bool {
    if !mockedObject.CheckConditionA() {
        return false
    }
    return mockedObject.CheckCondition1()
}

Another is:

func UnderTest(mockedObject *Widget) bool {
    if !mockedObject.CheckCondition1() {
        return false
    }
    return mockedObject.CheckConditionA()
}

A third is:

func UnderTest(mockedObject *Widget) bool {
    cA := mockedObject.CheckConditionA()
    c1 := mockedObject.CheckCondition1()
    return cA && c1
}

And a fourth is:

func UnderTest(mockedObject *Widget) bool {
    if TodayIsAnAlternateTuesday() {
        return mockedObject.CheckCondition1() && mockedObject.CheckConditionA()
    }
    return mockedObject.CheckConditionA() && mockedObject.CheckCondition1()
}

My test shouldn't have to care which of these is being used; I just want to check that the contract is fulfilled.

How do I set up the mocks for a test like this, where the code under test is allowed but not required to call a method?

For the test for the case where CheckConditionA() will return false, I need to set up something like mockedObject.EXPECT().CheckConditionA().Return(false). But some of these implementations will also require a mockedObject.EXPECT().CheckCondition1().Return(true) in order to work, or my test will fail due to unexpected calls. On the other hand, some will fail if I provide that EXPECT, due to missing calls.

Does GoMock just not support writing this kind of test? If not, what else could I be using that would let me test this function without constructing a real live Widget?

答案1

得分: 4

这个问题涉及到一个普遍的情境,根据这个答案,你实际上不需要使用Mocks,而是需要使用Stubs,它们是一种不强制要求被调用但如果被调用会返回正确结果的模拟对象。

GoMock的README实际上有一个关于构建Stubs而不是Mocks的整个章节,其中的要点是你需要在EXPECT()调用链的末尾使用AnyTimes()来使其变为可选项。

mockedObject.EXPECT().CheckConditionA().Return(false)
mockedObject.EXPECT().CheckCondition1().Return(true).AnyTimes()

不幸的是,该README章节只包含一个标题和两个代码示例,没有关于Stub是什么或为什么需要Stub的说明文字。这意味着如果你第一次接触基于模拟的测试是通过GoMock,你可能不会很快弄清楚这个概念。主要的GoMock文档中有关于AnyTimes()的一行描述,但没有关于Stub的概念的说明(或者关于Mocks的概念,他们似乎默认你已经听说过它们)。

英文:

This question gets at the problem in a general context, and following this answer to this answer shows that in this case you do not want Mocks; you actually want Stubs, which are mocks that don't insist on being called but if called will return the right things.

The GoMock README actually has a whole section on building stubs instead of mocks, the upshot of which is that you need to use AnyTimes() at the end of the EXPECT() call chain to make it optional.

mockedObject.EXPECT().CheckConditionA().Return(false)
mockedObject.EXPECT().CheckCondition1().Return(true).AnyTimes()

Unfortunately, that README section contains exactly one title and two code examples, with no text about what a stub is or why someone would want one, which means if your first exposure to mock-based testing is through GoMock you are not going to figure this out very quickly. The main GoMock docs have a one-line description of AnyTimes(), but nothing about stubs as a concept. (Or about mocks as a concept for that matter; they just sort of assume you've heard of them.)

huangapple
  • 本文由 发表于 2022年5月7日 06:53:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/72148121.html
匿名

发表评论

匿名网友

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

确定