Как переопределить действие ON_CALL по умолчанию только для одного EXPECT_CALL и вернуться к действию по умолчанию позже - PullRequest
0 голосов
/ 03 июля 2018

Я хотел бы протестировать метод моей системы, чье возвращаемое значение частично зависит от возвращаемого значения вызова какого-либо интерфейса подключения. В большинстве случаев я бы хотел, чтобы 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 голосов
/ 19 июля 2018

РЕДАКТИРОВАТЬ 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));

Таким образом, каждый раз, когда проверяемый метод open будет вызываться с аргументами "BAD_ADDRESS" и 20, он будет возвращать 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?

...