Qt测试框架的扩展
来源:互联网 发布:手机淘宝怎么更新 编辑:程序博客网 时间:2024/04/29 21:54
本文转载自Alex's Blog http://www.zshalex.com/blog/?p=127
在Qt中编写单元测试的基本流程:
- 创建一个类,并使其从QObject类继承下来。
- 编写测试函数。
- 使用QTEST_MAIN宏运行测试。
基于以上的原因,我们必须要在一个程序中运行所有的测试类,那应该如何实现这一功能呢?
既然运行单个测试是通过使用QTEST_MAIN宏来实现,那么我们就首先来看一下QTEST_MAIN宏的内容:
#define QTEST_MAIN(TestObject) \int main(int argc, char *argv[]) \{ \ QApplication app(argc,argv); \ QTEST_DISABLE_KEYPAD_NAVIGATION \ TestObject tc; \ return QTest::qExec(&tc,argc,argv); \}将宏展开后,它里面做的其实就是创建一个我们传入的测试类的实例,并调用QTest::qExec方法。既然如此,如果我们调用两次QTest::qExec,单元测试是否会运行两次呢?
仔细阅读Qt的文档,其中对QTest::qExec是这样描述的,如果运行以下的代码,那么MyFirstTestObject和MySecondTestObject都会被运行。
MyFirstTestObject test1;QTest::qExec(&test1);MySecondObject test2;QTest::qExec(&test2);但是QTest::qExec方法是不可重入的,同一时间只有一个测试可以被运行,就算是在不同的线程运行也是不被允许的。
OK现在我们已经确定可以在一个程序中运行多个单元测试,但是我们不可能在每次创建了一个测试类后,都手动的在main函数中添加运行测试的代码。如果手动添加,我们就会在mian函数中看到如下代码:
MyFirstTestObject test1;QTest::qExec(&test1);MySecondObject test2;QTest::qExec(&test2);MySecondObject test3;QTest::qExec(&test3); . . .MySecondObject testn;QTest::qExec(&testn);很显然这种方式也是非常不合理的,但是Qt的测试框架又不像CppUnit提供了TestSuite的功能,所以我们需要对Qt的测试框架进行扩展。
扩展测试框架的需求
我们所扩展的测试框架有一下几个核心的需求:
- 测试类的编写不能有太大的变化,不能嵌入太多与测试无关的代码,如果能做到无缝接入那是最好的。
- 必须能够自动运行所有的测试类。
- 可以根据使用者的要求,运行指定的单元测试,或者排除指定的单元测试。
扩展测试框架的设计与实现
运行测试主要分为两个步骤,首先要创建测试类的实例,然后调用QTest::qExec运行测试。
要想自动运行所有的测试,我们就需要一个列表来存放所有测试类的实例。另外由于要能自由的选择或排除某些单元测试,所以需要为每个测试类提供一个名称,以便之后的查找,所以可以使用Map去保存测试类,而不是列表。因此我们首先创建一个用于管理Map的类TestSuite。它的借口定义如下:
class TestSuite { public: static TestSuite* instance(); static void release(); TestSuite(); ~TestSuite(); void add(const QString &name, QObject *obj); int runTest(int argc, char** argv); const int totalTest(){return m_TotalTest;} const int errorCount(){return m_ErrorCount;} const int succCount(){return m_SuccCount;} const int runCount(){return m_RunCount;} const QStringList &errorTest(){return m_ErrorTest;} protected: void loadArg(int argc, char** argv); private: static TestSuite *m_TestSuite; //单元测试类的map QMap<QString, QObject*> map; //单元测试的总数 int m_TotalTest; //错误单元测试的个数 int m_ErrorCount; //正确单元测试的个数 int m_SuccCount; //运行的单元测试个数 int m_RunCount; //错误单元测试的名称列表 QStringList m_ErrorTest; //需要执行的单元测试的名称列表 QStringList m_IncludeTest; //不需要执行的单元测试的名称列表 QStringList m_ExcludeTest; //最终需要运行的单元测试的名称列表 QStringList m_RunTest; //getopt的配置 static const char *optString; static const struct option longOpts[]; };
其中最重要的两个方法是add与runTest。add会将指定的测试类的实例添加到map中,键值为name。runTest则会根据命令行参数运行指定的测试类。而命令行参数的分析使用getopt来分析,并在loadArg方法中实现。
另外TestSuite类使用了单体模式,可以通过TestSuite::instance()直接获取TestSuite类的实例。有了TestSuite类后,main函数中就只需要执行如下的代码,就能够自动运行所有的单元测试。
int main(int argc, char** argv) { QApplication app(argc, argv); int result = TestSuite::instance()->runTest(argc,argv) ; TestSuite::instance()->release(); return result; }
现在我们已经可以运行所有在map中的测试类,但是我们又要如何将测试类的实例添加map中呢?在这里我们参考了CppUnit的实现,在每个测试类中添加一个静态的指向当前类对象的指针,并在静态变量初始化时调用TestSuite类的add方法。
因此我们实现了一个TestCase类,并声明了两个宏,分别是DECLARE_TEST_CASE以及INIT_TEST_CASE。他们的实现如下:
//type表示类名 #define DECLARE_TEST_CASE(type) \ static type *tc; //type表示类名,name表示单元测试的名称 #define INIT_TEST_CASE(type,name) \ type *type::tc = new type(name); class TestCase : public QObject { Q_OBJECT public: explicit TestCase(const QString &name) { TestSuite::instance()->add(name,this); } };当我们要添加一个单元测试时,首先从TestCase类下继承一个类,例如:VideoTest。然后在头文件的public段中使用DECLARE_TEST_CASE宏去声明一个静态的变量,然后在cpp文件中使用INIT_TEST_CASE宏初始化静态变量。
//videotest.hclass VideoTest : public TestCase{ Q_OBJECTpublic: DECLARE_TEST_CASE(VideoTest); GetVideoRequestTest(const QString &name);};//videotest.cppINIT_TEST_CASE(VideoTest,"videoTest");VideoTest::VideoTest(const QString &name): TestCase(name){}
这样我们创建的测试类就会自动添加到TestSuite的map中,之后只需要像之前一样编写测试函数就可以了。
- Qt测试框架的扩展
- python的unittest测试框架的扩展浅谈
- 一种用于测试 XML 文档的 JUnit 扩展框架
- Qt的单元测试框架QTestLib----测试自定义的类。
- QT中状态机框架测试
- Qt:QSerialPort的多线程扩展
- 一种轻量级、可重用、可扩展的 OSGi 应用程序测试框架
- Dbunit(基于junit扩展的数据库测试框架。)vs Jmockit
- python单元测试框架nose的扩展nose-ittr支持传参测试
- Qt embedded的测试
- Qt的测试程序
- Qt的QGraphicsView框架
- Qt的QGraphicsView框架
- Qt的QGraphicsView框架
- Qt的QGraphicsView框架
- Qt的QGraphicsView框架
- Qt的QGraphicsView框架
- Qt的流框架
- 第八周作业任务一方案三
- 分页控件 AspNetPager 的使用
- 【零基础学习php二】 php 数据类型
- openfire插件开发续一
- window.showModalDialog以及window.open用法简介
- Qt测试框架的扩展
- UltraEdit高级教程之列模式、排序操作
- mysql 学习小结
- html标签
- bash技巧:求集合的交集、并集、差集、对称差集
- NetBeans无法激活J2ME组件的解决方法
- Android如何使用样式创建半透明窗体
- .net 开发模式和架构比较
- 艾瑞咨询:2011年中国移动互联网行业五大盘点