如何使用模拟框架测试 google 测试延迟后进行的调用
How can I test a call is made after a delay in google test using a mocking framework
我目前正在尝试评估不同的测试框架。使用模拟框架时(我倾向于 FakeIt,但 google 模拟也不错),我知道您可以使用 OS 的计时器调用来滚动自己的 "performance" 测试在调用函数之前和之后验证函数的性能。这不是我想要的。
我所拥有的是 类 在给定特定输入的情况下对输出实施延迟。例如:
- 输入 1 从低变高
- 输出 1 从低变高 在 1.5 秒后。
我希望能够在指定边界的地方做一些事情:
myMock.theInput();
EXPECT_CALL(myMock, theDelayedOutput())
.Times(1)
.Before(1.6sec)
.After(1.4sec);
特此说明,不支持 Before
和 After
行。这只是我更喜欢的简单语法的一个例子。
是否可以在进行输入调用和检查 EXPECT_CALL
之前在 windows 中仅实现一个 "delay" 函数?
这是部分方式 - 我仍然需要启动专有计时器。是这样的吗?
myMock.theInput();
windowSleep(1.4);
startTimer();
EXPECT_CALL(myMock, theDelayedOutput())
.Times(1)
endTimer();
ASSERT_TRUE(elapsedTime() <= 0.2);
我很确定这个解决方案适用于任何框架而无需修改框架:
myMock.theInput();
startTimer();
EXPECT_CALL(myMock, theDelayedOutput());
endTimer();
ASSERT_TRUE(elapsedTime() >= 1.4);
ASSERT_TRUE(elapsedTime() <= 1.6);
然后可以将其包装在宏中:
#define EXPECT_CALL_DELAYED(theMock, expectCall, lowerBound, upperBound) {\
startTimer();\
EXPECT_CALL(theMock, expectCall);\
endTimer();\
ASSERT_TRUE(elapsedTime() >= lowerBound);\
ASSERT_TRUE(elapsedTime() <= upperBound);\
}
那么最后的测试代码是:
myMock.theInput();
EXPECT_CALL_DELAYED(myMock, theDelayedOutput(), 1.4, 1.6);
或者,您可以遵守 DRY 原则和 pre-specify "window" 时间安排。这允许您进行测试,指定准确的时间,但没有重复自己每次都必须上下添加 0.1 秒缓冲区的缺点。
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);
不过我还没有测试过这些。如果有效,我会稍后更新并接受。
既然你用单元测试标记了问题:在单元测试中,你的测试不应该依赖于物理时间的流逝。这有多种原因:其中之一是您希望您的测试尽快 运行,所以您不希望延迟。另一个原因是开发环境(你的单元测试运行)的时间可能与目标环境完全不同,因为不同的硬件、操作系统、系统负载……
也就是说,涉及物理时间流逝的测试确实有意义,但它们将是对目标系统(或其某种模拟)的集成测试。在单元测试中,您将采用不同的方法:
你会嘲笑代表你的时钟的 functions/methods。例如,如果您的函数 funcA 通过 setTimer 设置了一个计时器,提供了一个在时间结束时调用的回调:
- 在您的某些测试中,您模拟了 setTimer 函数,调用了 funcA,并且作为测试通过标准的一部分,检查模拟的 setTimer 是否使用您期望的参数被调用。
- 在其他一些测试中,您直接从测试中调用回调函数以查看该函数是否正确运行。
在这两个单元测试案例中,您不需要等待物理时间过去。
也就是说,您当然可以将用于单元测试的相同测试框架也用于集成测试(即使它们的名称,如 JUnit,似乎表明它们仅用于单元测试)。而且,如果集成测试确实是您的目标,那么 Jeff Lamb 的建议肯定会有所帮助。
我目前正在尝试评估不同的测试框架。使用模拟框架时(我倾向于 FakeIt,但 google 模拟也不错),我知道您可以使用 OS 的计时器调用来滚动自己的 "performance" 测试在调用函数之前和之后验证函数的性能。这不是我想要的。
我所拥有的是 类 在给定特定输入的情况下对输出实施延迟。例如:
- 输入 1 从低变高
- 输出 1 从低变高 在 1.5 秒后。
我希望能够在指定边界的地方做一些事情:
myMock.theInput();
EXPECT_CALL(myMock, theDelayedOutput())
.Times(1)
.Before(1.6sec)
.After(1.4sec);
特此说明,不支持 Before
和 After
行。这只是我更喜欢的简单语法的一个例子。
是否可以在进行输入调用和检查 EXPECT_CALL
之前在 windows 中仅实现一个 "delay" 函数?
这是部分方式 - 我仍然需要启动专有计时器。是这样的吗?
myMock.theInput();
windowSleep(1.4);
startTimer();
EXPECT_CALL(myMock, theDelayedOutput())
.Times(1)
endTimer();
ASSERT_TRUE(elapsedTime() <= 0.2);
我很确定这个解决方案适用于任何框架而无需修改框架:
myMock.theInput();
startTimer();
EXPECT_CALL(myMock, theDelayedOutput());
endTimer();
ASSERT_TRUE(elapsedTime() >= 1.4);
ASSERT_TRUE(elapsedTime() <= 1.6);
然后可以将其包装在宏中:
#define EXPECT_CALL_DELAYED(theMock, expectCall, lowerBound, upperBound) {\
startTimer();\
EXPECT_CALL(theMock, expectCall);\
endTimer();\
ASSERT_TRUE(elapsedTime() >= lowerBound);\
ASSERT_TRUE(elapsedTime() <= upperBound);\
}
那么最后的测试代码是:
myMock.theInput();
EXPECT_CALL_DELAYED(myMock, theDelayedOutput(), 1.4, 1.6);
或者,您可以遵守 DRY 原则和 pre-specify "window" 时间安排。这允许您进行测试,指定准确的时间,但没有重复自己每次都必须上下添加 0.1 秒缓冲区的缺点。
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);
不过我还没有测试过这些。如果有效,我会稍后更新并接受。
既然你用单元测试标记了问题:在单元测试中,你的测试不应该依赖于物理时间的流逝。这有多种原因:其中之一是您希望您的测试尽快 运行,所以您不希望延迟。另一个原因是开发环境(你的单元测试运行)的时间可能与目标环境完全不同,因为不同的硬件、操作系统、系统负载……
也就是说,涉及物理时间流逝的测试确实有意义,但它们将是对目标系统(或其某种模拟)的集成测试。在单元测试中,您将采用不同的方法:
你会嘲笑代表你的时钟的 functions/methods。例如,如果您的函数 funcA 通过 setTimer 设置了一个计时器,提供了一个在时间结束时调用的回调:
- 在您的某些测试中,您模拟了 setTimer 函数,调用了 funcA,并且作为测试通过标准的一部分,检查模拟的 setTimer 是否使用您期望的参数被调用。
- 在其他一些测试中,您直接从测试中调用回调函数以查看该函数是否正确运行。
在这两个单元测试案例中,您不需要等待物理时间过去。
也就是说,您当然可以将用于单元测试的相同测试框架也用于集成测试(即使它们的名称,如 JUnit,似乎表明它们仅用于单元测试)。而且,如果集成测试确实是您的目标,那么 Jeff Lamb 的建议肯定会有所帮助。