GMock: как вернуть фиктивную переменную класса в качестве возвращаемого значения

Я впервые пытаюсь использовать GMock (фреймворк google mocking для c ++). У меня следующий класс:

class LocalCache
{
public:
  virtual time_t GetCurrentTime() = 0;
  virtual int AddEntry(const std::string key, std::string& value);
  virtual int GetEntry(const std::string key, std::string& value);
};

Метод GetEntry вызывает GetCurrentTime вызов. Я хотел бы поиздеваться над методом GetCurrentTime, чтобы я мог продвинуть часы в моем тесте, чтобы проверить старение записей, которое происходит как часть вызова GetEntry (пожалуйста, не спрашивайте меня, почему старение выполняется как часть GetEntry вызов ... это еще одно обсуждение :(). Вот мой макет класса:

class MockLocalCache : public LocalCache
{
public:
  using LocalCache::GetCurrentTime;
  MOCK_METHOD0(GetCurrentTime, time_t());

  MockLocalCache()
  : mCurrentTime(0)
  {
  }

  void EnableFakeTime()
  {
    ON_CALL(*this, GetCurrentTime()).WillByDefault(Return(mCurrentTime));
  }

  void SetTime(time_t now) { mCurrentTime = now; }

private:
  time_t  mCurrentTime;
};

TEST(MockTest, TimeTest)
{
  MockLocalCache mockCache;

  mockCache.EnableFakeTime();

  std::string key("mykey");
  std::string value("My Value");

  EXPECT_TRUE(mockCache.AddEntry(key, value));

  mockCache.SetTime(10);   // advance 10 seconds

  std::string expected;
  EXPECT_TRUE(mockCache.GetEntry(key, expected));
}

Когда я запускаю тест, я ожидал, что значение mCurrentTime вернет моя фиктивная функция GetCurrentTime. Однако я получаю следующий вывод ошибки:

GMOCK WARNING:
Uninteresting mock function call - taking default action specified at:
..../test_local_cache.cpp:62:
    Function call: GetCurrentTime()
          Returns: 0
Stack trace:

Был бы признателен, если бы кто-нибудь мог сообщить мне, что я делаю не так и как это исправить. Заранее спасибо.


person Dnj Abc    schedule 12.09.2015    source источник


Ответы (2)


Решение вашей проблемы - сделать это намного проще. Просто используйте EXPECT_CALL там, где вы ожидаете вызова вашей имитируемой функции:

class MockLocalCache : public LocalCache
{
public:
  MOCK_METHOD0(GetCurrentTime, time_t());
};

TEST(MockTest, TimeTest)
{
  MockLocalCache mockCache;

  std::string key("mykey");
  std::string value("My Value");

  EXPECT_TRUE(mockCache.AddEntry(key, value));

  EXPECT_CALL(mockCache, GetCurrentTime()).WillOnce(Return(10));   // advance 10 seconds

  std::string expected;
  EXPECT_TRUE(mockCache.GetEntry(key, expected));
}

Просто чтобы ответить, почему ваш пример не сработал - при этом вызове сохраняется текущее значение вашей переменной-члена - последующие изменения не имеют никакого эффекта:

ON_CALL(*this, GetCurrentTime()).WillByDefault(Return(mCurrentTime));

Посмотрите в google-mock-doc разницу между Return и Return(ByRef .. .

Вероятно - я не проверял это, вызывая значение члена набора, перед вызовом настройки это значение по умолчанию также будет работать - но, как я уже сказал, для вашего случая должен использоваться EXPECT_CALL:

 mockCache.SetTime(10);   // advance 10 seconds
 mockCache.EnableFakeTime();
person PiotrNycz    schedule 12.09.2015
comment
Спасибо! ваше предложение сработало. Сначала я попробовал EXPECT_CALL с WillRepeatedly (Return (myVariable)). Я установил это своим методом DoFakeTime. Я ожидал, что он вернет значение myVariable, когда моя программа вызовет GetCurrentTime. Однако это не сработало. Я закончил вызывать EXPECT_CALL с новым значением myVariable перед каждым моим AddEntry / GetEntry. Интересно, есть ли способ вызвать EXPECT_CALL один раз и заставить его прочитать возвращаемое значение из переменной. - person Dnj Abc; 12.09.2015
comment
@DnjAbc, если вы сделали WillRepeatedly перед установкой значения mCurrentTime, тогда Return action сохранит текущее значение mCurrentTime ... Вы можете попробовать с Return(ByRef(mCurrentTime)) - но это менее читабельно (я считаю), чем вызов ÈXPECT_CALL WillOnce ... Return ... `каждый раз, когда вам это нужно .... - person PiotrNycz; 13.09.2015

Просто для записи (и будущие люди, которые найдут этот вопрос, как и я), в то время как ответ PiotrNycz - лучший вариант, когда вы можете сделайте это (сохраняя тестовые значения непосредственно внутри тестов) - в некоторых случаях действительно необходимо вернуть «живое» возвращаемое значение из поля или переменной.

Соответствующая документация: здесь; особенно:

  • Return(field) не работает (делает копию текущего значения поля, когда действие определено)
  • Return(ByRef(field)) также не работает (работает точно так же, как указано выше, вопреки тому, что вы могли ожидать)
  • ReturnRef(field) не компилируется (поскольку возвращаемый тип не является ссылкой)
  • ReturnPointee(&field) работает (возвращает значение на момент фактического вызова метода)

Конечно, вы должны убедиться, что указатель остается действительным при каждом вызове метода, поскольку теперь он используется напрямую, а не копирует.

person Miral    schedule 21.05.2019
comment
Спасибо, вот что мне было нужно. Я всегда хочу использовать Return(ByRef(foo)) и застрять. Я бы хотел, чтобы GTest это уловил. - person Brandlingo; 03.06.2020