前两篇文章,小豆君主要对QTest的非Gui测试进行了介绍,既然Qt最强的是界面部分,那今天我们就来看下界面的单元测试,QTest是如何做的。
众所周知,既然使用界面,当然就需要和用户交互,比如,鼠标点击,在文本框中输入文本,既然要自动化测试,那必须将鼠标的点击事件,键盘的输入事件等进行模拟。
为此,QTest::keyClicks()模拟在该控件上输入按键序列,基本等同于输入字符串。此外,还也可以指定键盘组合按键,例如与ctrl,shift等按键的组合,并在每次单击按键后设置延迟(以毫秒为单位)。
类似的方式,还可以使用QTest::keyClick()、QTest::keyPress()、QTest::keyRelease()、QTest::mouseClick()、QTest::mouseDClick()、QTest::mouseMove()、QTest::mousePress()和QTest::mouseRelease()函数来模拟GUI事件。
下面的一个例子,我们主要介绍keyClicks的用法,其它类同。
如下图所示,在输入价格和成本后,自动显示利润。我们为该窗口类取名为CommodityWidget
1 创建CommodityWidget窗口
ui界面如上图所示。
commoditywidget.h
#ifndef COMMODITYWIDGET_H #define COMMODITYWIDGET_H #include <QWidget> namespace Ui { class CommodityWidget; } class CommodityWidget : public QWidget { Q_OBJECT public: friend class CommodityTest; explicit CommodityWidget(QWidget *parent = 0); ~CommodityWidget(); double costing() const; double price() const; double profit() const; private slots: void showProfit(); void on_line_price_textChanged(const QString &arg1); void on_line_costing_textChanged(const QString &arg1); private: Ui::CommodityWidget *ui; }; #endif // COMMODITYWIDGET_H
在头文件中的
friend class CommodityTest;
因为CommdityTest模拟键盘事件,需要直接访问ui界面中的控件,所以将它声明为CommodityWidget的友元类。
commoditywidget.cpp
#include "commoditywidget.h" #include "ui_commoditywidget.h" #include "commodity.h" CommodityWidget::CommodityWidget(QWidget *parent) : QWidget(parent), ui(new Ui::CommodityWidget) { ui->setupUi(this); } CommodityWidget::~CommodityWidget() { delete ui; } double CommodityWidget::costing() const { return ui->line_costing->text().toDouble(); } double CommodityWidget::price() const { return ui->line_price->text().toDouble(); } double CommodityWidget::profit() const { return ui->line_profit->text().toDouble(); } void CommodityWidget::showProfit() { double c = costing(); double p = price(); Commodity commodity("beer_1", "啤酒", c, p); ui->line_profit->setText(QString::number(commodity.profit())); } void CommodityWidget::on_line_price_textChanged(const QString &arg1) { showProfit(); } void CommodityWidget::on_line_costing_textChanged(const QString &arg1) { showProfit(); }
2 编写测试函数
在CommodityTest的头文件中添加如下槽函数
头文件:
private slots: //成本 void case1_gui_costing(); //价格 void case2_gui_price(); //利润 void case3_gui_profit();
源文件,注意这里添加了ui头文件:
#include "commoditytest.h" #include "commodity.h" #include "commoditywidget.h" #include "ui_commoditywidget.h" void CommodityTest::case1_gui_costing() { CommodityWidget w; //模拟按键,在键盘上输入成本 5.0 QTest::keyClicks(w.ui->line_costing, "5.0"); QCOMPARE(w.costing(), 5.0); } void CommodityTest::case2_gui_price() { CommodityWidget w; //模拟按键,在键盘上输入价格 7.2 QTest::keyClicks(w.ui->line_price, "7.2"); QCOMPARE(w.price(), 7.2); } void CommodityTest::case3_gui_profit() { CommodityWidget w; //模拟按键,在键盘上输入成本5.0,价格7.2 //最后比较利润是否为2.2 QTest::keyClicks(w.ui->line_costing, "5.0"); QTest::keyClicks(w.ui->line_price, "7.2"); QCOMPARE(w.profit(), 2.2); }
运行程序,如下图:
3 多数据测试Gui
这和上一节的多数据测试框架基本相同,只是这里换成了Gui,具体我就不讲了,不明白的可以参考上一篇文章QTest测试框架,多数据测试
下面直接上代码
头文件:
private slots: //利润 void case3_gui_profit(); void case3_gui_profit_data();
源文件:
void CommodityTest::case3_gui_profit() { QFETCH(QTestEventList, costing); QFETCH(QTestEventList, price); QFETCH(double, expected); CommodityWidget w; costing.simulate(w.ui->line_costing); price.simulate(w.ui->line_price); QCOMPARE(w.profit(), expected); } void CommodityTest::case3_gui_profit_data() { QTest::addColumn<QTestEventList>("costing"); QTest::addColumn<QTestEventList>("price"); QTest::addColumn<double>("expected"); QTestEventList costing1; QTestEventList price1; costing1.addKeyClicks("5.0"); price1.addKeyClicks("7.2"); QTest::newRow("normal") << costing1 << price1 << 2.2; QTestEventList costing2; QTestEventList price2; costing2.addKeyClicks("5.0"); price2.addKeyClicks("3.2"); QTest::newRow("negative") << costing2 << price2 << -1.8; }
运行如下:
现在假设CommdityWidget中的profit函数有bug,例如,我将toDouble改成了toInt,我们来看看会出现什么结果:
double CommodityWidget::profit() const { return ui->line_profit->text().toInt(); }
看,测试程序告诉我们在文件commoditytest.cpp的第88行出现问题,并且,正确的结果应该是2.2,而实际返回的结果是0,我们查看第88行,原来是profit函数有bug,再查看该函数的实现,原来是将toDouble写成了toInt,所以导致输出结果为0
前一阵,在群中我看到有一位朋友提出,由于项目需求,需要人不停的点击鼠标按键来获取某些数据,而今天介绍的Gui测试框架是完全可以实现的,并且只需要四五行代码就能完全胜任。
关于测试框架的内容大概就这么多了,但里面还有很多有意思的函数,如果有朋友感兴趣,可以直接查看Qt帮助文档就可以啦。
最后,小豆君再啰嗦几句,对于测试代码可能有很多人懒得去写,但是,一旦写好了测试程序,你的开发工作会变得相当轻松,每次修改或重构程序后,直接运行测试程序,它会很快的帮助你定位问题,而不是每次写完程序后,还要重复的去自己输入测试数据,在界面上各种点击。
测试代码,是真正做到了一键运行,检查工作全部交给电脑,而你也可以喝喝茶,休息休息啦。
更多分享请关注微信公众号:小豆君Qt分享,只要关注,便可加入C++\Qt交流群,一起学习,更可获得所有文章源码。