GTest 使用 官方文档 https://github.com/google/googletest/blob/master/googletest/docs/primer.md
GMock 使用 官方文档 https://github.com/google/googletest/blob/master/googlemock/README.md
QtTest 使用 官方文档 https://doc.qt.io/qt-5/qttest-index.html
QSignalSpy 使用 官方文档 https://doc.qt.io/qt-5/qsignalspy.html
Qt程序单元测试学习记录 : 上
0. 前言
0.1 单元的定义
0.2 角色工作体系
0.3 测试任务
Qt程序单元测试学习记录 :中
1. GTest/GMock 安装
1.1 GTest 下载安装(Linux)
1.2 GTest测试
1.3 GMock 安装(Linux)
2. GTest基本使用
2.1 GTest基本概念
2.2 GTest 断言
2.3 创建Test Suite
2.4 创建 Test Fixtures
2.5 调用GTest
2.6 编写main()
2.7 命令行输入测试
2.8 如何测试私有函数
3. GMock基本使用
3.1 什么是GMock
3.2 使用GMock
3.3 GMock案例
Qt程序单元测试学习记录 :下
4. GTest/GMock测试框架不足
5. Qt信号槽相关功能测试 QSignalSpy
5.1 什么是QSignalSpy
6. Qt UI相关功能测试 Qt Test
6.1 QtTest 介绍
6.2 QtTest 鼠标键盘模拟
7. 测试方法
7.1 组织产品代码和测试代码
7.2 测试工程中如何引入被测代码
7.3 解除类之间的依赖,何时mock
一般情况下GTest就够了,为什么要了解Qt Test
Qt Test优点:
Qt Test缺点:
也许不是最好的办法,但很多人方法和建议是使用GTest模板,配合QSignalSpy和 (Qt)UI模拟 来做测试。
可以看看
https://stackoverflow.com/questions/1524390/what-unit-testing-framework-should-i-use-for-qt
https://stackoverflow.com/questions/4879628/comparing-qtest-with-other-frameworks
QSignalSpy可以连接到任何对象的任何信号并记录其发射。 QSignalSpy本身是QVariant列表的列表。信号的每次发射都将在列表后面添加一个项目,其中包含信号的参数。
QSignalSpy spy(myPushButton, &QPushButton::clicked);
QSignalSpy spy(myPushButton, SIGNAL(clicked(bool)));
函数 | 描述 |
---|---|
QByteArray QSignalSpy::signal() const | 返回当前监听类型 |
bool QSignalSpy::wait(int timeout = 5000) | 启动事件循环,直到接收到给定信号为止。超时返回false |
bool QSignalSpy::isValid() const | 收到有效信号返回真 |
比如:
/**
* @brief TEST_F
* DataBackupTest 备份数据测试
*/
TEST_F(ManageViewTest, DataBackupTest) {
m_widget_->show();
m_widget_->backup_manager_->Set_back_paths_({"./"});
QSignalSpy spy(m_widget_, SIGNAL(SignalStartBackup()));
ASSERT_EQ(0, spy.count());
QTimer::singleShot(100, m_widget_, [ = ] {
m_widget_->yes_button->click();
});
QTest::mouseClick(m_widget_->ui->backup_button, Qt::LeftButton);
ASSERT_EQ(1, spy.count());
}
QSignalSpy spy(myCustomObject, SIGNAL(mySignal(int,QString,double)));
myCustomObject->doSomething(); // trigger emission of the signal
QList<QVariant> arguments = spy.takeFirst();
QVERIFY(arguments.at(0).type() == QVariant::Int);
QVERIFY(arguments.at(1).type() == QVariant::String);
QVERIFY(arguments.at(2).type() == QVariant::double);
QtTest类似GtTest,提供了一系列接口和宏用来做单元/集成测试。提供了几个初始化和析构函数,也有提供了自己的全局测试参数、各种断言以及跟cmaketest (CTest)对接的很多宏。
不过不用研究那么多测试框架,大家都大同小异。无非就是利用友元的特性创建测试函数。c++的测试框架GTest算是比较完善和资料很多的,QtTest虽然也不错但中文资料很少,官网说明也仅仅是使用介绍和基本例子。去github上下载小型开源软件的代码基本都是使用GTest,参考和借鉴别人思路更方便。
当然,QtTest还是要了解的。界面相关测试肯定要模拟鼠标键盘,QtTest就提供了一系列模拟接口方便做界面测试。CTest和QtTest一起用,无疑大大提升开发效率。
QtTest模拟键盘鼠标基本思路是:
函数 | 描述 |
---|---|
QTest::keyPress | 按下按键 |
QTest::keyRelease | 释放按键 |
QTest::keyClick | 点击键盘(按下并释放)。 |
QTest::keyClicks | 连续点击键盘(按下并释放多次)。 |
QTest::keySequence | 连续输入,相当于keyClicks。 |
QTest::Shortcut | 快捷键(如果快捷键一个按键,就相当于keyClick。快捷键是组合键就相当于keyClicks)。 |
函数 | 描述 |
---|---|
QTest::MousePress | 按下鼠标按钮。 |
QTest::MouseRelease | 释放鼠标按钮。 |
QTest::MouseClick | 单击鼠标按钮(按下并释放)。 |
QTest::MouseDClick | 双击鼠标按钮(按下并释放两次)。 |
QTest::MouseMove | 移动鼠标。 |
比如:
/**
* @brief TEST_F
* 增加用户测试
* 可以增加用户
* 两次密码错误
* 两次密码正确
*/
TEST_F(UserEditViewTest, AddUserTest) {
QString use_name = "add";
QSignalSpy spy(m_widget_, SIGNAL(SignalEditUserInfo(
const QString, const QString,
const EditType)));
m_widget_->show();
m_widget_->SetDialog(UserEditView::ADD, use_name);
ASSERT_EQ("", m_widget_->ui->username_edit->text());
// username_edit 可以输入
QTest::keyPress(m_widget_->ui->username_edit, Qt::Key_A);
QTest::keyPress(m_widget_->ui->username_edit, Qt::Key_D);
QTest::keyPress(m_widget_->ui->username_edit, Qt::Key_D);
ASSERT_EQ(use_name, m_widget_->ui->username_edit->text());
// 输入错误密码
QTest::keyPress(m_widget_->ui->password_edit_1, Qt::Key_1);
QTest::keyPress(m_widget_->ui->password_edit_2, Qt::Key_2);
QTest::mouseClick(m_widget_->ui->confirm_button, Qt::LeftButton);
ASSERT_EQ(0, spy.count());
// 输入正确密码
QTest::keyPress(m_widget_->ui->password_edit_2, Qt::Key_Backspace);
QTest::keyPress(m_widget_->ui->password_edit_2, Qt::Key_1);
QTest::mouseClick(m_widget_->ui->confirm_button, Qt::LeftButton);
ASSERT_EQ(1, spy.count());
QList<QVariant> arguments = spy.takeFirst();
ASSERT_EQ(arguments.at(0).toString(), use_name);
ASSERT_EQ(arguments.at(1).toString(), "1");
ASSERT_EQ(arguments.at(2).value<UserEditView::EditType>(), 1);
}
比如:
/**
* @brief TEST_F
* MouseClick 鼠标点击验证
*/
TEST_F(FoldControlViewTest, MouseClick) {
m_widget_->show();
QSignalSpy spy(m_widget_, SIGNAL(SignalsHiddenButtonClicked()));
QTestEventList events;
events.addMouseClick(Qt::LeftButton);
events.addDelay(200);
events.simulate(m_widget_->ui->pushButton);
events.simulate(m_widget_->ui->pushButton);
events.simulate(m_widget_->ui->pushButton);
ASSERT_EQ(spy.count(), 3);
}
我在github上拉一些完整的带测试工程的软件代码。
└── usr
├── 源码
│ │ └── 模块/功能/分类 A
│ │ │ │ └── A 相关源码
│ │ │ │ │ │ └── A 测试代码
│ │ └── 模块/功能/分类 B
│ │ │ │ └── B 相关源码
│ │ │ │ │ │ └── B 测试代码
│ │ └── 模块/功能/分类 C
│ │ │ │ └── C 相关源码
│ │ │ │ │ │ └── C 测试代码
├── 测试数据
│ │ │ │ └── ABC 测试数据
│ │ │ │ └── ABC 测试通用框架
└── usr
├── 源码
│ │ └── 模块/功能/分类 A
│ │ │ │ └── A 相关源码
│ │ └── 模块/功能/分类 B
│ │ │ │ └── B 相关源码
│ │ └── 模块/功能/分类 C
│ │ │ │ └── C 相关源码
├── 测试数据
│ │ │ │ └── ABC 测试数据
│ │ │ │ └── ABC 测试通用框架
│ │ │ │ └── ABC 测试代码
│ │ │ │ │ │ └── A 测试代码
│ │ │ │ │ │ └── B 测试代码
│ │ │ │ │ │ └── C 测试代码
一般有下边两种方法:
用过 gmock会发现当你使用ThirdService、ThirdLibrary时。由于gmock采用的是继承的方式,你需要自己重新实现一个ThirdService。
当你真正想把被测代码隔离开了的时候,才进行Mock。