Search code examples
c++unit-testinggoogletestgooglemockfakeit

How can I test a call is made after a delay in google test using a mocking framework


I'm currently trying to evaluate different testing frameworks. When using mocking frameworks (I'm leaning towards FakeIt, but google mock is good too), I know that you can roll your own "performance" testing by using the OS's timer calls before and after calling a function to verify the function's performance. This is not what I'm after.

What I do have are classes that implement delays on outputs given certain inputs. For example:

  • input 1 goes from low to high
  • output 1 goes from low to high after 1.5 seconds.

I'd like to be able to do something where I specify a boundary:

myMock.theInput();
EXPECT_CALL(myMock, theDelayedOutput())
  .Times(1)
  .Before(1.6sec)
  .After(1.4sec);

For clarification, the Before and After lines are not supported. This is just an example of what I'd prefer as an easy syntax.

Is it possible to just implement a "delay" function within windows between making the input call and before checking EXPECT_CALL?

That's part of the way - I'd still have to then start a proprietary timer. Something like this?

myMock.theInput();
windowSleep(1.4);
startTimer();
EXPECT_CALL(myMock, theDelayedOutput())
  .Times(1)
endTimer();
ASSERT_TRUE(elapsedTime() <= 0.2);

Solution

  • I'm pretty sure this solution will work for any framework without modifying the framework:

    myMock.theInput();
    startTimer();
    EXPECT_CALL(myMock, theDelayedOutput());
    endTimer();
    ASSERT_TRUE(elapsedTime() >= 1.4);
    ASSERT_TRUE(elapsedTime() <= 1.6);
    

    This can then be wrapped in a macro:

    #define EXPECT_CALL_DELAYED(theMock, expectCall, lowerBound, upperBound) {\
      startTimer();\
      EXPECT_CALL(theMock, expectCall);\
      endTimer();\
      ASSERT_TRUE(elapsedTime() >= lowerBound);\
      ASSERT_TRUE(elapsedTime() <= upperBound);\
    }
    

    The final test code is then:

    myMock.theInput();
    EXPECT_CALL_DELAYED(myMock, theDelayedOutput(), 1.4, 1.6);
    

    Alternately, you can adhere to the DRY principle and pre-specify a "window" of timing. This allows you to test, specifying exact timing, but without the downside of repeating yourself having to add a 0.1 second buffer up and down every time.

    EXPECT_CALL_DELAYED_SET_WINDOW(0.1);
    myMock.theInput();
    EXPECT_CALL_DELAYED(myMock, theDelayedOutput(), 1.5);
    myMock.theSecondInput();
    EXPECT_CALL_DELAYED(myMock, theSecondDelayedOutput(), 3.1);
    

    I haven't tested any of this, though. I'll update later and accept if this works.