如何仅对一个EXPECT_CALL覆盖默认的ON_CALL操作,以后再返回默认操作

时间:2018-07-03 16:26:41

标签: c++ googlemock

我想测试系统的方法,其返回值部分取决于对某种连接接口的调用的返回值。在大多数情况下,我希望IConnection在对true方法进行任何形式的调用时都返回open(_, _)。除了一种情况,当我明确测试连接失败的情况时。

示例:

/*
 * Some kind of network interface with method `open`
 */
class IConnection {
public:
    IConnection() = default;
    virtual ~IConnection() = default;
    virtual bool open(const std::string& address, int port) = 0;
};

class ConnectionMock: public IConnection {
public:
    MOCK_METHOD2(open, bool(const std::string& address, int port));
};

class MySystem {
public:
    MySystem() = delete;
    MySystem(std::shared_ptr<IConnection> connection): connection_(connection) {}
    bool doSth() {
        /*
         * Do some things, but fail if connection fails
         */
        bool connectionStatus = connection_->open("127.0.0.1", 6969);
        if (!connectionStatus) {
            return false;
        }
        // do other things
        return true;
    }
private:
    std::shared_ptr<IConnection> connection_;
};

TEST(MySystemShould, returnFalseIfFailedToOpenConnectionAndTrueIfSucceeded) {
    auto connectionMock = std::make_shared<NiceMock<ConnectionMock> >();
    ON_CALL(*connectionMock, open(_, _)).WillByDefault(Return(true));
    MySystem system(connectionMock);
    // if I don't specify Times test fill fail, because WillOnce automatically sets Times(1)
    EXPECT_CALL(*connectionMock, open(_, _)).Times(AnyNumber()).WillOnce(Return(false));
    /*
     * Commented code below is not a good solution - after expectation retires
     * the test will fail upon subsequent calls
     */
    //EXPECT_CALL(*connectionMock, open(_, _)).WillOnce(Return(false)).RetiresOnSaturation();
    ASSERT_FALSE(system.doSth());
    /*
     * Code bellow allows me to avoid the warning
     */
    //EXPECT_CALL(*connectionMock, open(_, _)).WillRepeatedly(Return(true));
    ASSERT_TRUE(system.doSth());
}

我当前解决方案的问题是,当EXPECT_CALL的覆盖变得饱和时,即使gmock返回到在ON_CALL上指定的默认操作,对open(_, _)的每次后续调用都会导致以下警告:

GMOCK WARNING:
/whatever.cpp:105: Actions ran out in EXPECT_CALL(*connectionMock, open(_, _))...
Called 2 times, but only 1 WillOnce() is specified - taking default action specified at:
/whatever.cpp:103:

即使我正在使用NiceMock。我可以通过将EXPECT_CALL指定为WillRepeatedly(Return(true))来摆脱警告,但这是我的代码在ON_CALL中的重复。

我想知道,如何仅对ON_CALL进行一次调用就覆盖IConnection::open指定的默认操作,然后又返回默认值,而不会导致gmock打印警告。完美的解决方案应类似于:

EXPECT_CALL(*connectionMock, open(_, _)).WillOnce(Return(false)).DisableExpectationAfterSaturation();

,但不存在。 RetiresOnSaturation不起作用,因为它饱和后无法通过测试(与ON_CALL指定的动作不匹配)。

1 个答案:

答案 0 :(得分:2)

编辑2
DoDefault()-功能非常接近问题中的要求。它指定EXPECT_CALL中的操作应返回到ON_CALL指定的默认操作:

using ::testing::DoDefault;

// Default action
ON_CALL(*connectionMock, open(_, _)).WillByDefault(Return(true));

// returns true once and then goes back to the default action
EXPECT_CALL(*connectionMock, open(_, _)
  .WillOnce(Return(false))
  .WillRepeatedly(DoDefault());

初始答案

如果IConnection::open的返回值取决于参数,则可以两次指定ON_CALL,但要使用不同的参数(或者用参数代替占位符):

ON_CALL(*connectionMock, open(_, _)).WillByDefault(Return(true));
ON_CALL(*connectionMock, open("BAD_ADDRESS", 20)).WillByDefault(Return(false));

因此,每当使用参数“ BAD_ADDRESS”和20调用模拟方法open时,它将返回false,否则返回true。

这是一个简单的例子:

using ::testing::_;
using ::testing::AnyNumber;
using ::testing::Return;

class A {
 public:
  virtual bool bla(int a) = 0;
};

class MOCKA : public A {
 public:
  MOCK_METHOD1(bla, bool(int));
};

TEST(ABC, aBABA) {
  MOCKA a;
  ON_CALL(a, bla(_)).WillByDefault(Return(false));
  ON_CALL(a, bla(1)).WillByDefault(Return(true));

  EXPECT_CALL(a, bla(_)).Times(AnyNumber());

  EXPECT_TRUE(a.bla(1));
  EXPECT_TRUE(a.bla(1));
  EXPECT_TRUE(a.bla(1));
  EXPECT_FALSE(a.bla(2));
  EXPECT_FALSE(a.bla(3));
  EXPECT_FALSE(a.bla(4));
}

编辑1 我认为现在我已经了解了问题所在,如果我做到了,那么解决方案将非常简单:

EXPECT_CALL(*connectionMock, open(_, _))
      .Times(AnyNumber())
      .WillOnce(Return(true))
      .WillRepeatedly(Return(false));

ConnectionMock::open内部调用MySystem::doSth时,无论参数是什么,它将一次返回true,然后始终返回false。在这种情况下,您也无需指定ON_CALL。还是肯定需要使用ON_CALL而不是EXPECT_CALL指定操作?