如何在GoogleTest中使用QTimers

sha*_*era 5 c++ qt googletest qtimer

我在单元测试期间遇到一些情况,我希望某些QTimer的超时在某个QObject中触发一些插槽.如何做到这一点以及此测试的一些常见缺陷并不是很明显.

sha*_*era 11

这种模式是我发现的作品.我怀疑它可能在某种程度上依赖于线程模型,因此我提供了一个YMMV的小注释.

假设你有一些

class Foo : public QObject{
  ...
  public:
  QTimer* _timer;

  public slots:
  virtual void onTimeout();
  ...
}
Run Code Online (Sandbox Code Playgroud)

为简单起见,让我们假装这是一个私有实现类,这就是暴露计时器的原因,而插槽是虚拟的,所以我们可以模拟它.

class MockFoo : public Foo{
public:
  MOCK_METHOD0(onTimeout, void());
}
Run Code Online (Sandbox Code Playgroud)

首先,当使用Qt中的QTimers和其他线程模型时,我们必须修改google test的'main'功能:

int main(int argc, char **argv) {
    QCoreApplication app(argc, argv);

    ::testing::InitGoogleTest(&argc, argv);
    int ret = RUN_ALL_TESTS();

    QTimer exitTimer;
    QObject::connect(&exitTimer, &QTimer::timeout, &app, QCoreApplication::quit);
    exitTimer.start();
    app.exec();
    return ret;
}
Run Code Online (Sandbox Code Playgroud)

接下来,在测试套件中:

TEST_F(Foo_Tests, onTimeout){
  MockFoo* foo{new MockFoo};
  //using Qt 5 convention, but do what you gotta do for signal spy in your setup
  QSignalSpy timeoutSpy(foo->_timer, &QTimer::timeout);
  QSignalSpy deleteSpy(foo, &QObject::destroyed);

  foo->_timer->setInterval(0);
  foo->_timer->setSingleShot(true);

  EXPECT_CALL(*foo, onTimeout());

  foo->_timer->start();

  EXPECT_TRUE(timeoutSpy.wait(100));
  foo->deleteLater();
  deleteSpy.wait(100);
}
Run Code Online (Sandbox Code Playgroud)

关于这一点的一些注意事项非常重要:

  1. 即使你已经在测试套件中的其他地方有一个MockFoo,你需要在这一个测试中创建和销毁一个.我怀疑这与QTimer和Qt的线程模型/事件循环有关.
  2. 假设你在别处做了类似的测试,它可能是一个不同的类,不同的套件,不同的命名空间,一切.如果您的EXPECT_CALL在此处未饱和(或其他一些谷歌测试测试),使用此模式的下一个测试将失败,但它会抱怨此测试的预期.
    • 例如:Bar_test失败:MockFoo :: onTimeout预计会被调用一次,实际上不被称为notisfied和active
  3. 在退出之前等待对象被销毁是很重要的.这允许Qt事件循环处理此对象上的挂起操作,即触发插槽的超时信号.
  4. 即使真正的程序使用非SingleShot计时器,在这里设置它也会简化它,以便不会多次调用插槽,从而扰乱测试.