是否可以同时依赖 gMock 中的预期调用?

Is it possible to depend expected calls in gMock together?

假设我想测试以下功能:

template<typename Pack>
bool is_valid(const Pack& pack) {
  if (pack.a() > 0)
    return pack.b();
  return false;
};

我会为Pack写一个小mocker如下:

class PackMock {
public:
    MOCK_METHOD(int, a, (), (const));
    MOCK_METHOD(bool, b, (), (const));
};

现在我尝试在测试函数中编写测试此方法:

TEST(MyTests, IsValidTest) {
    PackMock p;
    EXPECT_CALL(p, a())
        .WillOnce(Return(0))
        .WillOnce(Return(1))
        .WillOnce(Return(20));
    EXPECT_CALL(p, b())
        .WillOnce(Return(true))
        .WillOnce(Return(false))
        .WillOnce(Return(true));

    EXPECT_FALSE(is_valid(p));
    EXPECT_FALSE(is_valid(p));
    EXPECT_TRUE(is_valid(p));
}

乍一看,这段代码应该没问题。我有 3 个期望和我的嘲笑者 returns 3 个不同的值集,这就是实际应用程序 运行ning(数据方面)中发生的情况。

但是如果你运行这段代码,你会得到一个错误,提示不满足模拟者的期望(它说b()被调用两次时预计会调用3次)。原因是当第一次测试发生时,a() returns 0,并且由于 if 条件,根本没有调用 b()。所以最后,一切都搞砸了。

现在我想知道是否有办法将这些期望联系在一起,或者我应该始终根据代码的工作方式而不是我的数据的工作方式来设置我的期望?我个人认为后者应该是正确的。例如,如果在 mocker 中调用 a(),我们确保我们总是满足 b() 中的 1 个期望?或者例如,我们将这些期望链接在一起,仅当 a() > 0?

时才期望 b()

首先,您可以使用 testing::Sequence 实例按顺序跟踪模拟调用。您也可以直接为测试定义 InSequence,如 docs.

其次,您的第一个 EXPECT_CALL 不会调用 b() 模拟调用,因为 a() returns 0 然后永远不会评估 return pack.b().

作为 here 中的示例,我已经说明了如何在您的示例中使用 Sequence class,如下所示:

TEST(MyTests, IsValidTest) {
    
    // Setup the mock object
    PackMock p;

    // Keep track of the sequence of events 
    // using a sequence class
    testing::Sequence sequence;

    // # First is_valid() - a
    EXPECT_CALL(p, a())
        .InSequence(sequence)
        .WillOnce(Return(0));

    // # First is_valid() - b - not called
    // EXPECT_CALL(p, b())
    //     .InSequence(sequence)
    //     .WillOnce(Return(true));

    // # Second is_valid() - a
    EXPECT_CALL(p, a())
        .InSequence(sequence)
        .WillOnce(Return(1));

    // # Second is_valid() - b
    EXPECT_CALL(p, b())
        .InSequence(sequence)
        .WillOnce(Return(false));
        
    // # Third is_valid() - a
    EXPECT_CALL(p, a())
        .InSequence(sequence)
        .WillOnce(Return(20));

    // # Third is_valid() - b
    EXPECT_CALL(p, b())
        .InSequence(sequence)
        .WillOnce(Return(true));

    // Act - Initiate the calls and inject the 
    // mock object as a dependency
    
    // # First call - a() returns 0, 
    // thus returns false (but() b is not called)
    EXPECT_FALSE(is_valid(p));
    
    // # Second call - a() returns 1, 
    // thus returns false, because b returns false
    EXPECT_FALSE(is_valid(p));

    // # Third call - a() returns 20, 
    // thus returns true, because b returns true
    EXPECT_TRUE(is_valid(p));
}

这将提供以下输出:

Running main() from /opt/compiler-explorer/libs/googletest/release-1.10.0/googletest/src/gtest_main.cc
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from MyTests
[ RUN      ] MyTests.IsValidTest
[       OK ] MyTests.IsValidTest (0 ms)
[----------] 1 test from MyTests (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

因此,回答你的问题:

Is it possible to depend expected calls in gMock together?

是,使用 testing::Sequence class 或仅使用 testing::InSequence()

因此,如果您在测试中有相互依赖的调用,请使用序列 class 个实例到 link 它们。