QtTest(Benchmark)
【生活经历分享】华师国培 华师伴学 合同都是坑 消费者付款后无法退款
和华师国培签合同需小心,合同中都是保护华师的条款,没有保护消费者的条款。
收到钱,就算你因对培训质量不满意,也不能退款。因合同消费者维权肯定十分艰难。
华师伴学的授课方式是看录制的视频,不是真人现场教学。是否是您和孩子想要的学习方式?
各位打算报名的,交费要谨慎!
其他人在小红书上发的,转:
深圳市华师国培教育科技有限公司,黑心机构,大家擦亮眼睛,别被骗了,消费欺诈,虚假承诺,签合同各种坑,收到钱了不履行承诺不退款,乱扣费,维权艰难! - 小红书
【工作要用到的技术,之前记录在云笔记中,因时间太久已经不记得出处。如果有问题请联系我,会及时配合处理】
QtTest(Benchmark)
https://doc.qt.io/archives/qt-4.8/qtestlib-manual.html
https://doc.qt.io/archives/qt-4.8/qtestlib-tutorial.html
To create a test, subclass QObject and add one or more private slots to it. Each private slot is a testfunction in your test. QTest::qExec() can be used to execute all testfunctions in the test object.
单元测试注意事项(https://www.cnblogs.com/techiel/p/7942326.html)
1 单元测试类中建议不要出现私有成员,尤其是指针,同时不建议在测试函数中建立被测类的指针,而是直接建立被测类的对象,在测试结束后容易遗忘指针。若需要指针initTestCase中new,在cleanupTestCase中delete。
2 若某个测试函数中出现了new,一定记着delete,且务必让delete在第一个断言前出现,因为断言失败函数就回立刻结束,并把当前函数标记为测试失败。若delete在第一个断言之后,而第一个断言失败则不会执行之后的delete。
3 若测试类必须有私有成员,必须注意一个测试类中的所有函数公用私有成员,不会在每个测试之前刷新状态。
4 单例测试:若被测类为单例,欲对其内所有函数做单元测试,会出现测试第一个函数可以保证测试环境为初始状态,后续测试回因为单例的原因,导致测试时建立在之前操作后的环境下。欲解决此问题,需要删除单例。
******详细内容见本文后续的【单元测试工具QTestLib介绍】
Linux下使用Google test
一、安装Google test
git clone https://github.com/google/googletest.git
cd googletest
mkdir build
cd build
cmake .. // 请先确认 cmake 已经安装, 否则先执行: sudo apt-get install cmake
make
sudo make install
库文件安装在: /usr/local/lib/x86_64-linux-gnu 目录中(x86_64-linux-gnu 目录是安装时创建的), 如下:
zhyh@zhyh-vm:/usr/local/lib/x86_64-linux-gnu$ ls
cmake libgmock.a libgmock_main.a libgtest.a libgtest_main.a pkgconfig
头文件安装在 /usr/local/include 中, 如下:
zhyh@zhyh-vm:/usr/local/include/gtest$ ls
gtest-death-test.h gtest-message.h gtest-printers.h gtest-test-part.h
gtest.h gtest-param-test.h gtest_prod.h gtest-typed-test.h
gtest-matchers.h gtest_pred_impl.h gtest-spi.h internal
二、使用Google test
1. add程序: add.cc
#include
int add(int a, int b)
{
return a + b;
}
2. 为add程序编写单元测试用例: testAdd.cc
#include
extern int add(int a, int b);
TEST(testCase, test0)
{
EXPECT_EQ(add(2, 3), 5);
}
int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
3. 编译和链接程序
leo@ubuntu:~/code$ g++ add.cc testAdd.cc -lgtest -lpthread
leo@ubuntu:~/code$ ./a.out
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from testCase
[ RUN ] testCase.test0
[ OK ] testCase.test0 (0 ms)
[----------] 1 test from testCase (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (0 ms total)
[ PASSED ] 1 test.
使用: g++ add.cc testAdd.cc -lgtest -lpthread
编译和链接单元测试用例。
-lgtest 是链接 libgtest.a 库,另外也需要链接pthread。
可以看到gtest单元测试用例正常的运行起来。
4. gtest_main.a有什么用?
可以不用写自己的main函数,链接 libgtest_main.a 就可以了。
如上文的 testAdd.cc 中可以没有 main 函数:
#include
extern int add(int a, int b);
TEST(testCase, test0)
{
EXPECT_EQ(add(2, 3), 5);
}
编译命令为:
g++ add.cc testAdd.cc -lgtest -lgtest_main -lpthread
将libgtest_main.a链接进入程序。
单元测试工具QTestLib介绍
QTestLib
- QTestLib是Qt提供的一种针对基于Qt编写的程序或库的单元测试框架
- QTestLib提供了单元测试框架的基本功能,并提供了针对GUI测试的扩展功能
- 所有公共方法都在QTest命名空间中
- QTestLib是为了简化QT程序或库的单元测试工作而设计的
- 轻量级:QTestlib只包含6000行代码和60个导出符号
- 自包含:对于非GUI测试,QTestlib只需要Qt核心库的几个符号。
- 快速测试:QTestlib不需要特殊的测试执行程序,不需要为测试而进行特殊的注册。
- 数据驱动测试:一个测试程序可以在不同的测试数据集上执行多次。
- 基本的GUI测试:QTestlib提供了模拟鼠标和键盘事件的功能。
- 基准测试:QTestLIB支持基准测试并提供多种测量后端。
- IDE友好:QTestlib的输出信息可以被Visual Studio和KDevelop解析。
- 线程安全:错误报告是线程安全的、原子性的。
- 类型安全:对模板进行了扩展使用,防止由隐式类型转换引起的错误。
- 易扩展:用户自定义类型可以容易地加入到测试数据和测试输出中。
测试类
- 测试类需要继承与QObject类
- 每个私有槽函数就是一个测试函数
- 有4种私有槽不能作为测试函数,它们由测试框架执行,可为整个测试程序或当前测试函数进行初始化和清除操作
- initTestCase():在第一个测试函数执行前调用。
- cleanupTestCase():在最后一个测试函数执行后调用。
- init():在每一个测试函数执行前调用。
- cleanup():在每一个测试函数执行后调用。
- QTest::qExec(QObject* testClassObject)函数用于执行测试对象中所有的测试函数。
如果initTestCase()函数执行失败,任何测试函数都不会执行。如果init()函数执行失败,紧随其后的测试函数不会被执行,测试会继续处理下一个测试函数。
test.pro:
QT += testlib
QT -= gui
TARGET = tst
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += tst_mytest.cpp
DEFINES += SRCDIR=\\\"$$PWD/\\\"
tst_mytest.cpp
#include
#include
class MyTest : public QObject
{
Q_OBJECT
public:
MyTest();
private Q_SLOTS:
void test_Case1();
void test_Case2();
};
MyTest::MyTest()
{
}
void MyTest::test_Case1()
{
QVERIFY2(true, "Failure");
}
void MyTest::test_Case2()
{
QVERIFY(false);
}
QTEST_MAIN(MyTest)
#include "tst_mytest.moc"
编译:
qmake
make
运行:./tst
运行结果:
********* Start testing of MyTest *********
Config: Using QtTest library 5.5.0, Qt 5.5.0 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 4.9.1 20140922 (Red Hat 4.9.1-10))
PASS : MyTest::initTestCase()
PASS : MyTest::test_Case1()
FAIL! : MyTest::test_Case2() 'false' returned FALSE. ()
Loc: [tst_mytest.cpp(27)]
PASS : MyTest::cleanupTestCase()
Totals: 3 passed, 1 failed, 0 skipped, 0 blacklisted
********* Finished testing of MyTest *********
所有测试函数都运行成功:
********* Start testing of MyTest *********
Config: Using QtTest library 5.5.0, Qt 5.5.0 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 4.9.1 20140922 (Red Hat 4.9.1-10))
PASS : MyTest::initTestCase()
PASS : MyTest::test_Case1()
PASS : MyTest::test_Case2()
PASS : MyTest::cleanupTestCase()
Totals: 4 passed, 0 failed, 0 skipped, 0 blacklisted
********* Finished testing of MyTest *********
基准线:
void MyTest::test_Case2()
{
QVERIFY(true);
QBENCHMARK{
QVERIFY(true);
}
}
********* Start testing of MyTest *********
Config: Using QtTest library 5.5.0, Qt 5.5.0 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 4.9.1 20140922 (Red Hat 4.9.1-10))
PASS : MyTest::initTestCase()
PASS : MyTest::test_Case1()
PASS : MyTest::test_Case2()
RESULT : MyTest::test_Case2():
0.00011 msecs per iteration (total: 59, iterations: 524288)
PASS : MyTest::cleanupTestCase()
Totals: 4 passed, 0 failed, 0 skipped, 0 blacklisted
********* Finished testing of MyTest *********
测试数据集:
void MyTest::testQString_data()
{
QTest::addColumn("string");
QTest::addColumn("result");
QTest::newRow("lower") << "hello"<<"HELLO";
QTest::newRow("mix")<<"HellO"<<"HELLO";
QTest::newRow("upper")<<"HELLO"<<"HELLO";
}
void MyTest::testQString()
{
QFETCH(QString, string);
QFETCH(QString, result);
QCOMPARE(string.toUpper(), result);
}
- QTest::addColumn(name)添加列
——addColumn()函数定义两个为QString类型的元素列,分别取名为string和result
- QTest::newRow(name)<每组数据将成为测试表中的一个单独行
——通过newRow()函数添加了三条测试数据集,分别取名为lower、mix和upper

- 测试函数的关联数据函数具有相同的名称,并以_data结尾标识
- 通过宏QFETCH获取测试数据表中的测试数据,其第一个参数为元素列的类型,第二个参数为元素列名称(与数据表中定义一致)
- 通过宏QCOMPARE来比较函数执行返回的值与期望的值是否一致
Qt Quick测试
Qt Quick Test是一个用于QML应用程序的单元测试框架,测试用例是作为TestCase类型中的JavaScript函数编写的,执行顺序按照函数名称排序;测试函数名称必须写成test_xxx
tst_test.qml:
import QtQuick 2.3
import QtTest 1.0
TestCase {
name: “test”
function test_math() {
compare(2 + 2, 4, ”2 + 2 = 4”)
}
function test_fail() {
compare(2 + 2, 5, “2 + 2 = 5”)
}
}
通过C++驱动运行,tst_mytest.cpp:
#include
#include
#include
class MyTest : public QObject
{
Q_OBJECT
public:
MyTest() {}
public slots:
void qmlEngineAvailable(QQmlEngine *engine)
{
engine->rootContext()->setContextProperty("myContextProperty", QVariant(true));
}
};
QUICK_TEST_MAIN(MyTest)
#include "tst_mytest.moc"
.pro:加入qmltestcase
TEMPLATE = app
TARGET = tst_example
CONFIG += warn_on qmltestcase
SOURCES += tst_example.cpp
编译:
qmake
make
运行:./tst_example
运行结果:
********* Start testing of MyTest *********
Config: Using QtTest library 5.5.0, Qt 5.5.0 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 4.9.1 20140922 (Red Hat 4.9.1-10))
PASS : MyTest::initTestCase()
FAIL! : MyTest::test_fail() 2+2=5
Actual (): 4
Expected (): 5
Loc: [/home/dev/Desktop/test/tst_test.qml(9)]
PASS : MyTest::test_math()
PASS : MyTest::cleanupTestCase()
Totals: 3 passed, 1 failed, 0 skipped, 0 blacklisted
********* Finished testing of MyTest *********
多个tst_xxx.qml文件:
********* Start testing of MyTest *********
Config: Using QtTest library 5.5.0, Qt 5.5.0 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 4.9.1 20140922 (Red Hat 4.9.1-10))
PASS : MyTest::test::initTestCase()
FAIL! : MyTest::test::test_fail() 3+3=9
Actual (): 6
Expected (): 9
Loc: [/home/dev/Desktop/test/tst_2.qml(11)]
PASS : MyTest::test::test_math()
PASS : MyTest::test::cleanupTestCase()
PASS : MyTest::2::initTestCase()
FAIL! : MyTest::2::test_fail() 2+2=5
Actual (): 4
Expected (): 5
Loc: [/home/dev/Desktop/test/tst_test.qml(11)]
PASS : MyTest::2::test_math()
PASS : MyTest::2::cleanupTestCase()
Totals: 6 passed, 2 failed, 0 skipped, 0 blacklisted
********* Finished testing of MyTest *********
Qt Quick测试数据集
数据列表以”函数+_data”形式存在,init_data数据列表对应test_default_table函数,tag的内容会被测试架构打印出来,当语句检测失败时
import QtQuick 2.0
import QtTest 1.0
TestCase {
name: "DataTests"
function init_data() {
return [
{tag:"init_data_1", a:1, b:2, answer:3},
{tag:"init_data_2", a:2, b:4, answer:6},
]
}
function test_table_data() {
return [
{tag: "2 + 2 = 4", a:2, b:2, answer:4},
{tag: "2 + 6 = 8", a:2, b:2, answer:4},
]
}
function test_table(data) {
compare(data.a + data.b, data.answer);
}
function test_default_table(data) {
compare(data.a + data.b, data.answer);
}
}
运行结果:
********* Start testing of MyTest *********
Config: Using QtTest library 5.5.0, Qt 5.5.0 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 4.9.1 20140922 (Red Hat 4.9.1-10))
PASS : MyTest::DataTests::initTestCase()
PASS : MyTest::DataTests::test_default_table(init_data_1)
PASS : MyTest::DataTests::test_default_table(init_data_2)
PASS : MyTest::DataTests::test_table(2 + 2 = 4)
PASS : MyTest::DataTests::test_table(2 + 6 = 8)
PASS : MyTest::DataTests::cleanupTestCase()
Totals: 6 passed, 0 failed, 0 skipped, 0 blacklisted
********* Finished testing of MyTest *********
模拟键盘和鼠标按键
keyPress(),keyRelease(),keyClick()函数用于模拟键盘事件,该事件将被转发给具有焦点的对象
mouserPress(),mouseRelease(),mouseClick(),mouseDoubleClick(),mouseDoubleClickSequence()以及mouseMove()函数将用于鼠标事件模拟
import QtQuick 2.0
import QtTest 1.0
Rectangle {
width:50;height:50
focus: true
TestCase{
name: "KeyClick"
when: windowShown
function test_key_click() {
keyClick(Qt.Key_Left)
keyClick("a")
}
}
}
运行结果:
********* Start testing of MyTest *********
Config: Using QtTest library 5.5.0, Qt 5.5.0 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 4.9.1 20140922 (Red Hat 4.9.1-10))
PASS : MyTest::KeyClick::initTestCase()
PASS : MyTest::KeyClick::test_key_click()
PASS : MyTest::KeyClick::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted
********* Finished testing of MyTest *********
when属性:当需要运行测试案例时,需要把when属性置true
点击界面执行测试案例:
import QtQuick 2.0
import QtTest 1.0
Rectangle {
width:640;height:480
color: "cyan"
MouseArea{
id: area
anchors.fill: parent
}
TestCase {
when: area.pressed
function test_bar(){
verify(true);
}
}
}
运行结果:
********* Start testing of MyTest *********
Config: Using QtTest library 5.5.0, Qt 5.5.0 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 4.9.1 20140922 (Red Hat 4.9.1-10))
PASS : MyTest::initTestCase()
PASS : MyTest::test_bar()
PASS : MyTest::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted
********* Finished testing of MyTest *********
windowShown属性。界面显示后,被置为true
import QtQuick 2.0
import QtTest 1.0
import QtQuick.Controls 1.0
Button {
id: button
onClicked: text = "Clicked"
TestCase {
when: windowShown
function test_click(){
button.clicked();
compare(button.text,"Clicked");
}
}
}
运行结果:
********* Start testing of MyTest *********
Config: Using QtTest library 5.5.0, Qt 5.5.0 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 4.9.1 20140922 (Red Hat 4.9.1-10))
PASS : MyTest::initTestCase()
PASS : MyTest::test_click()
PASS : MyTest::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted
********* Finished testing of MyTest *********
SignalSpy: 统计button的click次数
import QtQuick 2.0
import QtTest 1.0
import QtQuick.Controls 1.0
Button {
id: button
SignalSpy {
id: spy
target: button
signalName: "clicked"
}
TestCase {
function test_click() {
compare(spy.count, 0)
button.clicked();
compare(spy.count, 1)
}
}
}
参考文章:
https://doc.qt.io/archives/qt-5.8/qtest.html
https://doc.qt.io/qt-5/qml-qttest-testcase.html#data-driven-tests
https://doc.qt.io/archives/qt-4.8/qtestlib-manual.html
https://doc.qt.io/archives/qt-4.8/qtestlib-tutorial.html
注意事项:
1、单元测试类中建议不要出现私有成员,尤其是指针,同时不建议在测试函数中建立被测类的指针,而是直接建立被测类的对象,在测试结束后容易遗忘指针。若需要指针initTestCase中new,在cleanupTestCase中delete。
2、若某个测试函数中出现了new,一定记着delete,且务必让delete在第一个断言前出现,因为断言失败函数就回立刻结束,并把当前函数标记为测试失败。若delete在第一个断言之后,而第一个断言失败则不会执行之后的delete。
3、若测试类必须有私有成员,必须注意一个测试类中的所有函数公用私有成员,不会在每个测试之前刷新状态
4、单例测试:若被测类为单例,欲对其内所有函数做单元测试,会出现测试第一个函数可以保证测试环境为初始状态,后续测试会因为单例的原因,导致测试时建立在之前操作后的环境下。欲解决此问题,需要删除单例。

浙公网安备 33010602011771号