EXPECT_CALL(模拟,f(N))vs f(K)后跟f(N)

时间:2019-05-14 16:07:03

标签: c++ googlemock

我试图了解EXPECT_CALL的工作原理,并且遇到了奇怪的行为(我认为很奇怪)。假设我的代码做到了这一点(假设有一个模拟并且f(int)是它的方法,还假设SomeNiceMock是NiceMock):

void SomeMock::f(int) { ... }
NiceMock<SomeMock> someNiceMock;
void runCycle(int n) { someNiceMock.f(n); }

现在,如果在测试中,我将执行以下操作

EXPECT_CALL(someNiceMock, f(2)).Times(AtLeast(1));
runCycle(1);
runCycle(2);
::testing::Mock::VerifyAndClearExpectations(&mock);

我得到一个错误,假设f(int)被调用为2,但被调用为1。

Expected: to be called at least once
Actual: never called - unsatisfied and active

如果我这样做的话:

runCycle(1);
EXPECT_CALL(someNiceMock, f(2)).Times(AtLeast(1));
runCycle(2);
::testing::Mock::VerifyAndClearExpectations(&mock);

一切正常。

我可以忍受这种行为,只是我不了解其背后的原因。 someNiceMock是一个NiceMock,因此只要实际使用预期参数调用f(int),它就不应抱怨使用其他预期参数调用f(int)。第二次调用runCycle(2)确实调用了f(2)。那么,为什么不仅仅忽略调用f(1)并且测试失败呢?是这样的,如果我为一个NiceMock甚至指定一个EXPECT_CALL如果此调用将使用不同的参数(但是稍后将使用适当的参数进行另一个调用),则测试将失败?认为这是一个NiceMock并实际上在两种情况下都发生了对f(2)的调用,这不是直觉吗?

编辑:那我该如何测试这种行为?假设我有一些数字生成器,我想测试一下,当它被调用10次时它至少返回3次5(而且我不在乎其他结果。我希望这样编码(对不起,如果我搞砸了语法,我不太擅长Google模拟):

struct INumberGeneratorSink {
    virtual void consumeNumber(int number) = 0;
};

struct NumberGeneratorSink : public INumberGeneratorSink  {
    void consumeNumber(int number) override { ... }
};

struct NumberGeneratorSinkMock : public INumberGeneratorSink  {
    MOCK_METHOD1(consumeNumber, void(int number));
};

void numberGeneratorFunction(INumberGeneratorSink &sink)
{
    for (int i = 0; i < 10; i++)
    {
        sink.consumeNumber(getNumberFromSomewhere());
    }       
}

NumberGeneratorSinkMock sinkMock;
NiceMock<NumberGeneratorSinkMock> niceSinkMock;

EXPECT_CALL(niceSinkMock, consumeNumber(5)).Times(AtLeast(3));
numberGeneratorFunction(niceSinkMock);

我该如何编码这样的东西?如果存在一些语法错误,请纠正我,但是我的问题更像是-如果我只在乎将该消耗数字以值5调用3次,而我不在乎其余如何编码呢?我必须写类似的东西吗?

// not sure about syntax for Any(),
// maybe it doesn't exist and has to be AtLeast(1)
EXPECT_CALL(niceSinkMock, consumeNumber(_)).Times(Any()); 
EXPECT_CALL(niceSinkMock, consumeNumber(5)).Times(AtLeast(3));

那行得通吗?首先EXPECT_CALL是否会完全匹配所有内容,并且即使从不使用5作为参数调用invokeNumber,测试也会通过吗?

1 个答案:

答案 0 :(得分:2)

我完全同意,gmock可能会对期望感到困惑。 ;-)

1)为什么以下操作失败?

EXPECT_CALL(someNiceMock, f(2)).Times(AtLeast(1));
runCycle(1);
runCycle(2);

如果模拟方法没有EXPECT_CALL而是被调用,则Google Mock将打印警告。要取消显示此警告,可以使用NiceMock。但是,如果存在EXPECT_CALL,它将并且应该失败。

2)为什么以下通过?

runCycle(1);
EXPECT_CALL(someNiceMock, f(2)).Times(AtLeast(1));
runCycle(2);

简单的答案:必须先编写EXPECT_CALL。但是这种编写测试的方法不应该是通常的方法。

3)处理多个期望的解决方案

来自gmock cook book

  

”“默认情况下,当调用模拟方法时,Google Mock将按照定义的相反顺序搜索期望,并在出现期望时停止   找到与参数匹配的积极期望”

您上次被截断的代码几乎是正确的。 Times(Any())的正确实现是忽略它。

EXPECT_CALL(someNiceMock, f(_));
EXPECT_CALL(someNiceMock, f(2)).Times(1);
runCycle(1);
runCycle(2);

还请注意,您的模拟“ SomeMock”需要“模拟”方法。 例如:

class SomeMock {
public:
  MOCK_CONST_METHOD1(f, void(int i));
};