GMock: How to return mock class variable as the return value
Asked Answered
E

2

7

I am trying to use GMock (google mocking framework for c++) for the first time. I have the following class:

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);
};

The GetEntry method invokes GetCurrentTime call. I'd like to mock the GetCurrentTime method so that I can advance the clock in my test to test the aging out of entries which happens as part of the GetEntry call (please don't ask me why the aging is being done as part of GetEntry call... that's another discussion :( ). Here's my mock class:

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));
}

When I run the test, I expected the mCurrentTime value to be return by my mock GetCurrentTime function. However, I get the following error output:

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

Would appreciate it if someone can let me know what I am doing wrong and how to fix it. Thanks in advance.

Eleen answered 12/9, 2015 at 1:40 Comment(0)
G
5

The solution to your problem is to make it in much simpler way. Just use EXPECT_CALLwhere you expect call to your mocked function:

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));
}

Just to answer why your example did not work - with this call, the current value of your member variable is stored - later change to it has no effect:

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

Look in google-mock-doc for difference between Return and Return(ByRef...

Probably - I did not check this, calling set member value, before calling setting this default would also work - but as I said - for your case EXPECT_CALL shall be used:

 mockCache.SetTime(10);   // advance 10 seconds
 mockCache.EnableFakeTime();
Giffard answered 12/9, 2015 at 11:27 Comment(2)
Thanks! your suggestion worked. First I tried the EXPECT_CALL with WillRepeatedly(Return(myVariable)). I set this up my my DoFakeTime method. I expected it to return myVariable value when ever my program called GetCurrentTime. However, that did not work. I ended calling EXPECT_CALL with a new value for myVariable before each of my AddEntry/GetEntry. I wonder if there a way to call EXPECT_CALL once and force it to read the return value from the variable.Eleen
@DnjAbc if you did WillRepeatedly before setting the value of mCurrentTime then Return action store the current value of mCurrentTime... You can try with Return(ByRef(mCurrentTime)) - but that less readable (im my opinion) than calling ÈXPECT_CALL WillOnce ... Return...` every time you need this....Giffard
H
7

Just for the record (and future people finding this question, like me), while PiotrNycz's answer is the best option when you can do it (keeping test values directly within tests) -- in some cases it really is necessary to return a "live" return value from a field or variable.

The appropriate documentation is here; in particular:

  • Return(field) doesn't work (it makes a copy of the field's current value when the action is defined)
  • Return(ByRef(field)) also doesn't work (it does exactly the same as above, contrary to what you might expect)
  • ReturnRef(field) doesn't compile (because the return type isn't a reference)
  • ReturnPointee(&field) does work (it returns the value as of the time the method is actually called)

Of course, you have to ensure that the pointee remains valid whenever the method is called, since it's now being used directly instead of making a copy.

Homeric answered 21/5, 2019 at 2:58 Comment(1)
Thanks, that's what I needed. I always want to use Return(ByRef(foo)) and get stuck. I wish GTest would catch that.Shrunken
G
5

The solution to your problem is to make it in much simpler way. Just use EXPECT_CALLwhere you expect call to your mocked function:

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));
}

Just to answer why your example did not work - with this call, the current value of your member variable is stored - later change to it has no effect:

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

Look in google-mock-doc for difference between Return and Return(ByRef...

Probably - I did not check this, calling set member value, before calling setting this default would also work - but as I said - for your case EXPECT_CALL shall be used:

 mockCache.SetTime(10);   // advance 10 seconds
 mockCache.EnableFakeTime();
Giffard answered 12/9, 2015 at 11:27 Comment(2)
Thanks! your suggestion worked. First I tried the EXPECT_CALL with WillRepeatedly(Return(myVariable)). I set this up my my DoFakeTime method. I expected it to return myVariable value when ever my program called GetCurrentTime. However, that did not work. I ended calling EXPECT_CALL with a new value for myVariable before each of my AddEntry/GetEntry. I wonder if there a way to call EXPECT_CALL once and force it to read the return value from the variable.Eleen
@DnjAbc if you did WillRepeatedly before setting the value of mCurrentTime then Return action store the current value of mCurrentTime... You can try with Return(ByRef(mCurrentTime)) - but that less readable (im my opinion) than calling ÈXPECT_CALL WillOnce ... Return...` every time you need this....Giffard

© 2022 - 2024 — McMap. All rights reserved.