当前位置:  编程技术>软件工程/软件设计
本页文章导读:
    ▪深入跟踪MFC程序的执行流程       在MFC程序设计的学习过程中最令人感到难受,甚至于有时会动摇学习者信心的就是一种对于程序的一切细节都没有控制权的感觉。这种感觉来源于学习者不知道一个MFC程序是如何运行起来的.........
    ▪gtest使用      首先#include <gtest/gtest.h>,当然工程的头文件路径要设置正确 1.简单测试TEST Cpp代码 #include <gtest/gtest.h>   int Factorial( int n )   {     if(n==2) return 100; //故.........
    ▪gtest框架的介绍与应用        【摘要】在本文中,作者根据之前使用gtest框架进行测试的经验,总结了一些使用方式和案例。 在这些案例中,我们可以了解到gtest框架的基本使用方法以及在我们日常测试中的应用,同.........

[1]深入跟踪MFC程序的执行流程
    来源: 互联网  发布时间: 2013-11-19

在MFC程序设计的学习过程中最令人感到难受,甚至于有时会动摇学习者信心的就是一种对于程序的一切细节都没有控制权的感觉。这种感觉来源于学习者不知道一个MFC程序是如何运行起来的(即一个MFC程序的执行流程)和MFC程序的设计思想和机制,即使是写过Windows程序的学习者,也会感到非常迷惘并且无从下手。而这种感觉的出现会使大家认为自己离开了书本上的例子就无法设计编制程序。下面我就来说一说一个MFC具体是如何被执行的。在阅读本文之前,你要有一定的Windows程序设计基础,知道Windows程序的运行流程,如不清楚,可先看看我写的这篇文章——解说一个简单的Win32程序。

一、单文档项目特点简述
以一个在VS2010中建立的一个单文档MFC程序来例子,深入跟踪MFC执行流程。工程名为MFCSDI,工程建立步骤不在这样详述。

当一个SDI(单文档)程序建立之后,我们会看到程序为我们生成了四个类:CAboutDlg、CChildView、CMainFrame、CMFCSDIApp。在它们的头文件中可以看到CCAboutDlg是从CDialogEx类派生出来的,用来显示一个对话框窗口,该对话框用来显示与此程序相关的版本信息。CMainFrame由类CFrameWndEx派生而来,用为表示一个程序的框架。CHildView类由类CWnd派生而来,用于单文档程序的显示。CMFCSDIApp类由类CWinAppEx派生而来,用于表示一个MFC程序,在每一个MFC程序中都有一个C+工程名+App的类(本例子中为CMFCSDIApp),它定义了一个全局对象theApp,它是一个应用程序对象,它就代表着这个程序。一个MFC程序有且只有一个这样的从CWinAppEx派生出来的类,也有且仅有一个从从CWinAppEx派生出来的类(如这里的CMFCSDIApp)所实例化的对象。

由此可见,这个MFC单文档程序并不像之前所说的Win32程序那样有一条清晰的主线。一个Windows程序从WinMain函数开始,经过注册窗口类、创建窗口、显示和刷新窗口才使得该程序的窗口界面为用户可见,然后建立进行消息循环,用户对此界面所作的任何操作都会被Windows作为消息传递给程序的窗口函数,并由窗口函数对消息进行分类处理,这些工作都是被 WinMain函数独自包办的。但在MFC程序中WinMain函数的地位被CWinApp类取代了,它所负责的全部初始化工作和对消息解释及分派都有 CWinApp类的内部函数来完成,但是WinMain仍然存在,并且扮演着驾驭CWinApp的角色。但我们在生成的所有文件的代码中,也找不到WinMain函数。而且这几个类之间是通过什么联系起来,组成一个Windows程序的呢?

二、在WinMain执行前初始化的全局变量theApp
前面说过,theApp是一个应用程序对象,它就代表着这个程序。一个MFC程序有且只有一个这样的从CWinAppEx派生出来的类,也有且仅有一个从从CWinAppEx派生出来的类(如这里的CMFCSDIApp)所实例化的对象。因为它是一个全局变量,根据C++的特点,它可以在WinMain函数执行前进行自己的初始化。

所以,要构造theApp对象就要调用其构造函数,由于CMFCSDIApp的基类为CWinAppEx,CWinAppEx的基类为CWinApp,由于要构造子类,就要先构造父类,即要构造theApp对象就要先调用CWinApp的构造函数来构造父类,CWinApp的构造函数对theApp的一些参数作初始化。

三、调用WinMain函数
构造完theApp这个全局对象后,就进入WinMian函数,它的代码在mfc代码所在目录下的appmodul.cpp文件中,这个函数名为_tWinMain,咋一看与我们在Win32所用的WinMain函数的名字不一样,其实_tWinMain是一个宏,到它的定义处一看,就知道它代表的正是WinMain,它的写法与我们在Win32程序中的WinMain函数是一样的。这个_tWinMain会调用一个函数AfxWinMain,这个函数在文件winmain.cpp中定义,而这个函数会有一条语句pThread->InitInstance(),pThread是一个窗口线程的指针,它的值由函数AfxGetThread()所得,根据多态性的原理,pThread会获得一个指向子类的指针,所以它会调用CMFCSDIApp类的成员函数CMFCSDIApp::InitInstance(),这个函数会初始化一些程序运行所需要的资源。

四、注册窗口
初始化一些所需的资源之后,就要对窗口类进行注册。MFC会调用函数AfxEndDeferRegisterClass注册窗口类,该函数的定义在文件wincore.cpp中。在Win32中时,我们需要设计一个窗口类,但是MFC已为我们设计好一个默认的窗口类,这里我们进行注册就行。

五、产生窗口
注册完窗口类之后,就会调用CMainFrm类的成员函数CMainFrm::PreCreateWindow来创建窗口,而这个函数会去调用它的父类的成员函数CFrameWnd::PreCreateWindow来创建窗口,在这个函数中可以对一些MFC设计好的默认的窗口类作一些修改,然后会调用AfxEndDeferRegisterClass函数进行窗口类的注册。之后,就会调用CFrameWnd::Create函数进行窗口的创建,该函数的定义在文件winfrm.cpp中,而该在窗口创建过程中该函数又会调用CWnd::CreateEx函数来对窗口进行创建,CWnd::CreateEx定义在文件wincore.cpp中。窗口创建完成后,会调用ShowWindow函数和UpdateWindow函数显示窗口,这两个函数在函数CMFCSDIApp::InitInstance()中被调用(在文件MFCSDI.cpp中)。

在一个程序中可以见到PreCreateWindow会被调用很多次,这是因为我们产生一个程序时会注册很多个窗口,如工具栏,按钮等,每创建一个窗口都要调用AfxEndDeferRegisterClass函数来进行窗口类注册,所以这两个函数就被多次地调用了。

六、建立消息循环
回到一开始所说的AfxWinMain函数中,里面有一条语句nReturnCode = pThread->Run();其实这就是建立消息循环。在文件thrdcore.cpp中可以找到它的定义(CWinThread::Run),它会循环调用函数PumpMessage(同样定义在文件thrdcore.cpp中),PumpMessage函数又会调用函数AfxInternalPumpMessage(在文件thrdcore.cpp中),它会调用函数GetMessage,它就等同于我们Win32程序中的函数GetMessage,然后调用函数::TranslateMessage,::DispatchMessage这与我们所写的Win32程序是一致的。

七、窗口过程
MFC在窗口类注册时就给它指定了一个默认的窗口过程,在函数AfxEndDeferRegisterClass(wincore.cpp)中有如下语句,wndcls.lpfnWndProc = DefWindowProc;把窗口过程指定为默认的窗口过程,而MFC则会通过消息映射转换处理过程,使我们的程序能响应不同的消息。

八、窗口的销毁
MFC程序的死亡相对于初生来说要简单的多,主要是以下几步: 
  1.使用者通过点击File/Close或程序窗口由上角的叉号发出WM_CLOSE消息。 
  2.程序没有设置WM_CLOSE处理程序,交给默认处理程序。 
  3.默认处理函数对于WM_CLOSE的处理方式为调用::DestoryWindow,并因而发出WM_DESTORY消息。 
  4.默认的WM_DESTORY处理方式为调用::PostQuitMessage,发出WM_QUIT。 
  5.CWinApp::Run收到WM_QUIT后结束内部消息循环,并调用ExinInstance函数,它是CWinApp的一个虚拟函数,可以由用户重载。 
  6.最后回到AfxWinMain,执行AfxWinTerm,结束程序。

    
[2]gtest使用
    来源: 互联网  发布时间: 2013-11-19
首先#include <gtest/gtest.h>,当然工程的头文件路径要设置正确

1.简单测试TEST

Cpp代码
  • #include <gtest/gtest.h>   
  • int Factorial( int n )   
  • {   
  •   if(n==2) return 100; //故意出个错,嘻嘻   
  •   return n<=0? 1 : n*Factorial(n - 1);   
  • }   
  • //用TEST做简单测试   
  • TEST(TestFactorial, ZeroInput) //第一个参数是测试用例名,第二个参数是测试名:随后的测试结果将以"测试用例名.测试名"的形式给出   
  • {   
  •    EXPECT_EQ(1, Factorial(0));  //EXPECT_EQ稍候再说,现在只要知道它是测试两个数据是否相等的就行了。   
  • }   
  •   
  • TEST(TestFactorial, OtherInput)   
  • {   
  •    EXPECT_EQ(1, Factorial(1));   
  •    EXPECT_EQ(2, Factorial(2));   
  •    EXPECT_EQ(6, Factorial(3));   
  •    EXPECT_EQ(40320, Factorial(8));   
  • }   
  •   
  • int main(int argc, char* argv[])   
  • {   
  •    testing::InitGoogleTest(&argc,argv); //用来处理Test相关的命令行开关,如果不关注也可不加   
  •    RUN_ALL_TESTS();  //看函数名就知道干啥了   
  •        std::cin.get();   //只是让它暂停而已,不然一闪就没了   
  •       return 0;   
  • }  
  • #include <gtest/gtest.h>
     int Factorial( int n )
     {
      if(n==2) return 100; //故意出个错,嘻嘻
      return n<=0? 1 : n*Factorial(n - 1);
     }
     //用TEST做简单测试
     TEST(TestFactorial, ZeroInput) //第一个参数是测试用例名,第二个参数是测试名:随后的测试结果将以"测试用例名.测试名"的形式给出
     {
      EXPECT_EQ(1, Factorial(0));  //EXPECT_EQ稍候再说,现在只要知道它是测试两个数据是否相等的就行了。
     }
    
     TEST(TestFactorial, OtherInput)
     {
      EXPECT_EQ(1, Factorial(1));
      EXPECT_EQ(2, Factorial(2));
      EXPECT_EQ(6, Factorial(3));
      EXPECT_EQ(40320, Factorial(8));
     } 
    
     int main(int argc, char* argv[])
     {
      testing::InitGoogleTest(&argc,argv); //用来处理Test相关的命令行开关,如果不关注也可不加
      RUN_ALL_TESTS();  //看函数名就知道干啥了
          std::cin.get();   //只是让它暂停而已,不然一闪就没了
          return 0;
     }

    2.多个测试场景需要相同数据配置的情况,用TEST_F

    Cpp代码
  • //用TEST_F做同配置的系列测试   
  • typedef std::basic_string<TCHAR> tstring;   
  • struct FooTest : testing::Test {   
  •   //这里定义要测试的东东   
  •    tstring strExe;   
  •   //可以利用构造、析构来初始化一些参数   
  •    FooTest() {}   
  •   virtual ~FooTest() {}   
  •   
  • //如果构造、析构还不能满足你,还有下面两个虚拟函数   
  • virtual void SetUp() {   
  •   // 在构造后调用   
  •    strExe.resize(MAX_PATH);   
  •    GetModuleFileName(NULL, &strExe[0], MAX_PATH);   
  • }   
  •   
  • virtual void TearDown() { }   // 在析构前调用   
  • };   
  •   
  • tstring getfilename(const tstring &full)  //偶写的从完整路径里取出文件名的函数(路径分隔符假定为'\\')   
  • {   
  • return full.substr(full.rfind(_T('\\')));   
  • }   
  •   
  • tstring getpath(const tstring &full)   //偶写的从完整路径里取出路径名的函数(Windows路径)   
  • {   
  • return full.substr(0, full.rfind(_T('\\')));   
  • }   
  •   
  • TEST_F(FooTest, Test_GFN) //测试getfilename函数   
  • {   
  • EXPECT_STREQ(_T("Projectexe"), getfilename(strExe).c_str());   
  • }   
  •   
  • TEST_F(FooTest, Test_GP) //测试getpath函数   
  • {   
  • EXPECT_STREQ(_T("D:\\Code\\libs\\google\\gtest-1\\BCC_SPC\\bcc\\ex"), getpath(strExe).c_str());   
  • }   
  •   
  • int main(int argc, TCHAR
        
  • [3]gtest框架的介绍与应用
        来源: 互联网  发布时间: 2013-11-19

     

    【摘要】在本文中,作者根据之前使用gtest框架进行测试的经验,总结了一些使用方式和案例。 在这些案例中,我们可以了解到gtest框架的基本使用方法以及在我们日常测试中的应用,同时也能促进我们对于百度的btest的了解。在我们之后的测试工作中,可以根据各个项目的特点以及gtest、btest等测试框架的功用,进行协调和因势利导,将更多框架产品用于我们的测试工作中,使我们的测试工作更加正规、更加高效、更加可依赖。由于作者能力有限,文中如果有一些不够清晰不够全面的地方,欢迎指正。

    【关键词】gtest, cxxtest, TestSuite, TestCase

     

    1、引言

    之所以要向大家介绍测试框架,是因为我们在测试的过程中,会越来越体会到测试规范化与可重用性的重要。一方面,我们要让测试的过程可管理化,要让我们自己、RD、其他同学都能够看懂我们测的是什么,是怎么来测的;另一方面,我们不可能一个项目总是一两个同学一直测下去,如果换到其他同学,需要有一种方式让这种传承更加通用、更加高效。如果能够让一个项目组写测试case使用相同的框架、相同的风格,那么可以避免我们的测试过于山寨,可以让测试的设计和执行更加正规化。

    我们选择一些第三方的开源的测试框架也是有原因的:

    首先,如果我们的公司内部自己实现了一套测试框架,固然可定制性更高,但是之后就会发觉相当于把测试框架重新做了一遍,业界有很多测试框架,已经并且正在接受全世界程序员的使用、检测和改进,比我们公司内部一个项目的力量要大很多。

    其次,正因为这些测试框架是开源的,所以虽然这些框架已经提供了比较完善和简单易用的功能,如果我们需要加上额外的功能,仍然可以在上面进行二次开发,相当于站在了巨人的肩膀上。

    现在百度ATD团队也在gtest的基础上发布了btest,可以让gtest的使用更加便捷;btest的运行方式和btest是一致的,如果我们对gtest有更多的了解,那么使用btest也会更加得心应手。下文中的所有的内容都适用于btest。

     

    2、cppunit, cxxtest与gtest框架

    在进入百度后,我主要测试的项目是一个后台服务程序,在测试自动化方面做的主要工作是使用shell, python以及c++编写一些客户端、桩模块以及测试驱动程序,来测试这个后台服务程序的各种行为是否符合我们的预期,并且在屏幕中打印出结果:Pass或者Fail. 很幸运的,我后来承担了一个lib库中的一个接口函数的测试,这个接口函数主要是进行URL的解析,虽然这个接口函数的代码量不多,测试工作量也不大,但是却是我之前没有接触过的测试范畴。我接到这个项目时,首先的想法就是这种函数级别的测试非常类似于RD经常要做的单元测试,我可以使用一些单元测试框架来规范我的测试,提高测试的效率,并且使得我的测试case、测试代码等等的复用性更高。

    于是我就开始调研使用哪一个测试框架来进行我的测试。与Java语言中的JUnit或者testNG不同,C和C++中的单元测试框架并没有这么普遍的工具,有一点百花齐放的局面。

    我首先调研的是cppunit,这是一个与JUnit类似的框架,我在大学时也接触过,但是这个框架很陈旧了,并且有着一些缺点,例如一些类可以消失,一些类名应该修改,一些宏定义应该修改,帮助很少很乱等,这里就不再一一赘述,感兴趣的同学可以百度一下。因为cppunit有着一系列的缺点,cppunit的鼻祖之一重写了一套C/C++单元测试框架,这就是cxxtest。与cppunit相比,可以说cxxtest具有如下一些优点:不需要RTTI(运行时间类型信息);不需要成员模板功能;不需要异常处理;不需要任何外部函数库(包括内存管理、文件/控制台的输入/输出和图形库等);它完全是作为一套头文件的集合而进行发布的。另外由于由于cppunit带有Make文件, 所以只能用在主要的操作系统中,而应用到不常见操作系统中源代码及Make文件修改的工作量就会很大。cxxtest不带Make文件, 所以也可用于其他操作系统中,具有更好的可移植性和可用性。Gtest虽然也使用Makefile文件,但是通过libtool动态加载来实现,可以支持Supports Linux, Windows, Mac OS, 以及其他操作系统。

    当然cxxtest也有一些缺点,例如需要用到perl或者python对测试代码的头文件进行文法扫描,生成可执行代码,准备工作比较麻烦。并且cxxtest的绝大多数功能都可以使用gtest来完成,所以一般情况下就更加推荐gtest,一步到位。在百度ATP的自动化测试工具推荐名单中,gtest也是作为一个推荐的工具来给出的,并且指出了cxxtest的使用可以转为用gtest代替。下面先给大家列出一些cxxtest和gtest之间的比较,这样大家就有个直观的印象。

     

    图1 gtest和cxxtest的比较

    在这次lib库的测试中,因为com组的qa同事使用的是cxxtest框架,所以我也使用了cxxtest框架,但是根据从网上了解的gtest的种种优点,我也学习了gtest的使用,在这里就主要向大家推荐gtest的使用,这样更能一步到位,免得总是不停地更换测试框架。

    首先介绍一下gtest测试框架。这是Google的开源C++单元测试框架,是遵循 New BSD License (可用作商业用途)的开源项目。据说google内部的大多数C++代码都已经使用这个测试框架进行单测,gtest 可以支持绝大多数大家所熟知的平台。Gtest的使用较为方便,与 CppUnit 不同的是,gtest 可以自动记录下所有定义好的测试,不需要用户通过列举来指明哪些测试需要运行。

    要了解gtest测试框架,一个很方便的学习资源就是gtest的官方网站 http://code.google.com/p/googletest/ . 在这个网址,读者也可以下载到gtest的源代码,用于阅读学习乃至在上面的改进。在本文中,我先根据自己的使用经验,给大家做一些介绍。

     

    3、gtest 简单示例

    我们就开始着手试一下gtest的基本功能吧,首先从http://code.google.com/p/googletest/ 这个网站下载下gtest的压缩包,解压到Linux环境的测试机器。然后我们进入解压后的gtest目录,首先 ./configure 然后 make ,然后我们就可以使用这个gtest测试框架了。

    我们写一段简单的例子,try.cpp:

    //try.cpp

    #include "gtest/gtest.h"

     

    namespace 

    {

    // The fixture for testing class Foo.

    class FooTest : public ::testing::Test 

    {

     protected:

      FooTest() 

    {

        // You can do set-up work for each test here.

      }

      virtual ~FooTest() 

    {

        // You can do clean-up work that doesn't throw exceptions here.

      }

      virtual void SetUp() 

    {

        // Code here will be called immediately after the constructor (right

        // before each test).

      }

      virtual void TearDown() 

    {

        // Code here will be called immediately after each test (right

        // before the destructor).

      }

      // Objects declared here can be used by all tests in the test case for Foo.

    };

     

    // Tests that the Foo::Bar() method does Abc.

    TEST_F(FooTest, ZeroEqual) 

    {

      EXPECT_EQ(0,0);

    }

     

    // Tests t

        
    最新技术文章:
     




    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3