可不可不要这么样徘徊在目光内 / 你会察觉到我根本寂寞难耐 / 即使千多百个深夜曾在梦境内 / 我有吻过你这毕竟并没存在 / 人声车声开始消和逝 / 无声挣扎有个情感奴隶 / 是我多么的想她 / 但我偏偏只得无尽叹谓 / 其实每次见你我也着迷 / 无奈你我各有角色范围 / 就算在寂寞梦内超出好友关系 / 唯在暗里爱你暗里着迷 / 无谓要你惹上各种问题 / 共我道别吧别让空虚使我越轨 /

发布新日志

  • LoadRunner下DLL的调用

    2007-08-25 00:12:45

     

    LoadRunner下DLL的调用
    这篇是LoadRunner测试的注意,可能跟开发没关系,不过也是用C++编程,解决大家性能测试的问题,就发过来,希望给大家带个新思想,如果有性能测试的问题,我还是能帮上忙的咯,呵呵
    场景介绍
    最近在做类似于QQ的通信工具的性能测试时发现了一些问题,现总结出来与大家分享一下。希望大家在使用LoadRunner时不仅仅停在只是录制/播放角本,而全面提升角本的编程技术,解决复杂场景。
    本次测试中碰到的问题是这样的,在消息的传送过程中遇到了DEC加密的过程,LoadRunner录制到的全是加密的消息,比如我录制了某一个用户的登陆,发送消息,退出,但由于是加密的,只能单个用户使用,但如果我想并发多少个用户就存在很多问题,最直接的一个问题就是用户名是加密的,密码是加密的,当然你可以说让程序那里注掉加密的代码进行明码的测试,当然也是一种办法。但程序组提出了要使用更真实的方法来模拟,这时就必需使用下面介绍的方法。
    一开始是直接把API移植到LoadRunner中来,不过由于加密算法异常复杂,有几层循环,而角本是解释执行的,进行一次加密运算可能需要好几分钟,当然在角本里可以把角本本身运行的时间去掉,但这样做显然没有直接调用DLL来的效率高。由于程序组比较忙,所以无法提供DLL给测试,所以测试完成了DLL的编写,并在LoadRunner中调用成功,高效的完成了用户信息加密,参数关联,成功的完成了测试。
    动态链接库的编写
      在Visual C++6.0开发环境下,打开FileNewProject选项,可以选择Win32 Dynamic-Link Library建立一个空的DLL工程。
      1. Win32 Dynamic-Link Library方式创建Non-MFC DLL动态链接库

      每一个DLL必须有一个入口点,这就象我们用C编写的应用程序一样,必须有一个WINMAIN函数一样。在Non-MFC DLL中DllMain是一个缺省的入口函数,你不需要编写自己的DLL入口函数,用这个缺省的入口函数就能使动态链接库被调用时得到正确的初始化。如果应用程序的DLL需要分配额外的内存或资源时,或者说需要对每个进程或线程初始化和清除操作时,需要在相应的DLL工程的.CPP文件中对DllMain()函数按照下面的格式书写。
     
    BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
    {
    switch( ul_reason_for_call )
    {
    case DLL_PROCESS_ATTACH:
    break;
    case DLL_THREAD_ATTACH:
    break;
    case DLL_THREAD_DETACH:
    break;
    case DLL_PROCESS_DETACH:
    break;
    default:
    break;
    }
    return TRUE;
    }
      
    参数中,hMoudle是动态库被调用时所传递来的一个指向自己的句柄(实际上,它是指向_DGROUP段的一个选择符);ul_reason_for_call是一个说明动态库被调原因的标志,当进程或线程装入或卸载动态链接库的时候,操作系统调用入口函数,并说明动态链接库被调用的原因,它所有的可能值为:DLL_PROCESS_ATTACH: 进程被调用、DLL_THREAD_ATTACH: 线程被调用、DLL_PROCESS_DETACH: 进程被停止、DLL_THREAD_DETACH: 线程被停止;lpReserved为保留参数。到此为止,DLL的入口函数已经写了,剩下部分的实现也不难,你可以在DLL工程中加入你所想要输出的函数或变量了。

      我们已经知道DLL是包含若干个函数的库文件,应用程序使用DLL中的函数之前,应该先导出这些函数,以便供给应用程序使用。要导出这些函数有两种方法,一是在定义函数时使用导出关键字_declspec(dllexport),另外一种方法是在创建DLL文件时使用模块定义文件.Def。需要读者注意的是在使用第一种方法的时候,不能使用DEF文件。下面通过两个例子来说明如何使用这两种方法创建DLL文件。

      1)使用导出函数关键字_declspec(dllexport)创建MyDll.dll,该动态链接库中有两个函数,分别用来实现得到两个数的最大和最小数。在MyDll.h和MyDLL.cpp文件中分别输入如下原代码:
     
    //MyDLL.h
    extern "C" _declspec(dllexport) int desinit(int mode);
    extern "C" _declspec(dllexport) void desdone(void);
    extern "C" _declspec(dllexport) void des_setkey(char *subkey, char *key);
    extern "C" _declspec(dllexport) void endes(char *block, char *subkey);
    extern "C" _declspec(dllexport) void dedes(char *block, char *subkey);
    //MyDll.cpp
    #include"MyDll.h"
    //这里我用了比较大小的函数代替了我要实现的函数
    int desinit(int a, int b)
    {
    if(a>=b)return a;
    else
    return b;
    }
    int desdone(int a, int b)
    {
    if(a>=b)return b;
    else
    return a;
    }
    该动态链接库编译成功后,打开MyDll工程中的debug目录,可以看到MyDll.dll、MyDll.lib两个文件。LIB文件中包含DLL文件名和DLL文件中的函数名等,该LIB文件只是对应该DLL文件的"映像文件",与DLL文件中,LIB文件的长度要小的多,在进行隐式链接DLL时要用到它。读者可能已经注意到在MyDll.h中有关键字"extern C",它可以使其他编程语言访问你编写的DLL中的函数。
    LoadRunner调用动态链接库
    上面完成动态链接库开发后,下面就介绍动态链接库如何被LoadRunner进行调用,其实也是很简单的。在LoadRunner中的DLL调用有局部调用与全局调用,下面介绍局部调用。
    首先把你编译的DLL放在角本路径下面,这里是MyDll.dll,MyDll.lib.然后在Action中使用
    lr_load_dll("MYDll.dll"),此函数可以把DLL加载进来,让你调用DLL里面的函数,而DLL中的运算是编译级的,所以效率极高,代码样例如下:
    #include "lrs.h"
    Action()
    {
    //
    int nRet = 6;
    char srckey[129];
    memset(srckey, 'a', 128);
    lr_message(lr_eval_string(srckey));
    lr_load_dll("MyDLL.dll");
    nRet = desinit(5,8);
    lr_message("比较的结果为%d",nRet);
    return 0;
    }
    运行结果
    比较的结果为8

    全局的动态链接库的调用则需要修改mdrv.dat,路径在LoadRunner的安装目录下面(LoadRunner/dat directory);在里面修改如例:
    [WinSock]
    ExtPriorityType=protocol
    WINNT_EXT_LIBS=wsrun32.dll
    WIN95_EXT_LIBS=wsrun32.dll
    LINUX_EXT_LIBS=liblrs.so
    SOLARIS_EXT_LIBS=liblrs.so
    HPUX_EXT_LIBS=liblrs.sl
    AIX_EXT_LIBS=liblrs.so
    LibCfgFunc=winsock_exten_conf
    UtilityExt=lrun_api
    ExtMessageQueue=0
    ExtCmdLineOverwrite=-WinInet No
    ExtCmdLineConc=-UsingWinInet No
    WINNT_DLLS=user_dll1.dll, user_dll2.dll, ...
    //最后一行是加载你需要的DLL
    这样你就可以在LR中随意的调用程序员写的API函数,进行一些复杂的数据加密,准备的一些操作,进行复杂的测试。同时如果你觉的有大量高复杂的运算也可以放在DLL中进行封装,以提高效率。

  • 原创]使用QTP对TXT文件进行读取和写入

    2007-08-22 12:32:32

    2007-08-19 13:44:18 / 个人分类:QTP

    c:\test.txt

    原来是
    ####
    orcal=true
    ####

    ####
    #MySQL=true
    #####

    改为
    ####
    #orcal=true
    ####

    ####
    MySQL=true
    #####

     

    使用函数:

    Function Replace_str(oldstr,newstr)
    Set fso = CreateObject("scrīpting.FileSystemObject")
    Set MyFilebak = fso.CreateTextFile("c:\testbak.txt", True)
    MyFilebak.close
    Set CreFile = fso.GetFile("c:\test.txt")
    CreFile.Copy ("c:\testbak.txt")
    Set MyFilebak=fso.OpenTextFile("c:\testbak.txt", 1, false)
    Set theFile = fso.OpenTextFile("c:\test.txt", 2, True)
    Do While MyFilebak.AtEndOfStream <> True
          retstring = MyFilebak.ReadLine
              If retstring=oldstr Then
                      theFile.WriteLine newstr

                      else
                                theFile.WriteLine retstring
              End If

       Loop
       MyFilebak.close
    Set DelFile = fso.GetFile("c:\testbak.txt")
    DelFile.Delete
       theFile.Close
       ReadEntireFile = retstring
    End Function

    Call Replace_str("#orcal=true","orcal=true")
    Call Replace_str("MySQL=true","#MySQL=true")

     

  • 在VC下使用CppUnit做单元测试的简单步骤

    2007-08-21 23:25:38

    1.取得Cppunit发行版本,下载的是cppunit-1.10.2
    2.使用INSTALL-WIN32.txt,
    3.查看examples中的例子,观看其配置。

    Libraries:
    ----------

    All the compiled libraries and DLL can be found in the 'lib' directory.
    Most libraries can be build from src/CppUnitLibraries.dsw workspace.

    lib\:
    cppunit.lib    : CppUnit static library "Multithreaded DLL"
    cppunitd.lib   : CppUnit static library "Debug Multithreaded DLL"
    cppunit_dll.dll   : CppUnit dynamic library (DLL) "Multithreaded DLL"
    cppunit_dll.lib   : CppUnit dynamic import library "Multithreaded DLL"
    cppunitd_dll.dll  : CppUnit dynamic library (DLL) "Debug Multithreaded DLL"
    cppunitd_dll.lib  : CppUnit dynamic import library "Debug Multithreaded DLL"
    qttestrunner.dll  : QT TestRunner dynamic library (DLL) "Multithreaded DLL"
    qttestrunner.lib  : QT TestRunner import library "Multithreaded DLL"
    testrunner.dll   : MFC TestRunner dynamic library (DLL) "Multithreaded DLL"
    testrunner.lib   : MFC TestRunner import library "Multithreaded DLL"
    testrunnerd.dll   : MFC TestRunner dynamic library (DLL) "Debug Multithreaded DLL"
    testrunnerd.lib   : MFC TestRunner import library "Debug Multithreaded DLL"
    testrunneru.dll   : MFC Unicode TestRunner dynamic library (DLL) "Multithreaded DLL"
    testrunneru.lib   : MFC Unicode TestRunner import library "Multithreaded DLL"
    testrunnerud.dll  : MFC Unicode TestRunner dynamic library (DLL) "Debug Multithreaded DLL"
    testrunnerud.lib  : MFC Unicode TestRunner import library "Debug Multithreaded DLL"
    TestRunnerDSPlugIn.dll : The add-in you register in VC++.


    A. 新建一个MFC应用程序
                                                      B. 在“工具”-选项-目录

                           
                                                          C. 在工程配置里面选择RTTI

                           

    Link下加入 Debug\cppunitd.lib Debug\testrunnerd.lib ,记得把这辆个文件从cppunit-1.10.2\lib拷出来,把相应的dll文件也拷到debug目录下

            D. 在App初始化函数中App::InitInstance()的开头加入
     #include <cppunit/ui/mfc/TestRunner.h>
    #include <cppunit/extensions/TestFactoryRegistry.h>
    在其中加入  
    CppUnit::MfcUi::TestRunner runner;
    runner.addTest( CppUnit::TestFactoryRegistry::getRegistry().makeTest() );
     runner.run();   
    记得把原来的窗口注掉,不然调用的还是原来的窗口。

    E. 加入要测的类叫XXX
    我们起这个类的测试叫testXXX 类

    #include <cppunit/TestCase.h>
    #include <cppunit/extensions/HelperMacros.h>

    #include "testXXX .h"


    class testXXX : public CppUnit::TestCase 
    {
     CPPUNIT_TEST_SUITE(testXXX );
      CPPUNIT_TEST(testcase1);   //这里就是我们的testcase的函数原型名字
      CPPUNIT_TEST(testcase2);
     CPPUNIT_TEST_SUITE_END();

    public:
     void setUp();
     void tearDown();

    protected:
     void testcase1();//声明我们的测试函数
     void testcase2();

    private:
     testXXX *fixture;

    };

    其cpp文件必须要有

    CPPUNIT_TEST_SUITE_REGISTRATION(testXXX);
    然后
    void testXXX::setUp()
    {
     fixture = new testXXX();//当然要按照实际的类构造你的测试对象了
    }
     
    void testXXX::tearDown()
    {
     delete fixture;
     fixture = NULL;//析构你的测试对象
    }

    下面就是你的测试函数

    void testXXX::testcase1()
    {
         CPPUNIT_ASSERT(condition1);//如果condition1为false激发这个assert
         CPPUNIT_ASSERT_MESSAGE(”msg“ , condition2);
        ……
    }

    第二个类似这样就可以
    运行结果类似于下面的窗口

                        

            ok,好多东西我还没有使用,今天就到这里,不过感觉挺好设置的,还是觉得比较麻烦了些,在MinGW Developer Studio不知道能否使用,我还没有使用过,赶明儿试试。

  • ShortCUT - 一个简短的c++单元测试框架

    2007-08-21 23:22:41

      1. CppUnit是xUnit系列中的c++实现版本,它是从JUnit移植过来的,第一个移植版本由Michael Feathers完成,安装cppunit,你可以在此下载cppunit的最新版本,最新版本是CppUnit release 1.12.0,安装方法,现解压,然后到文件夹下找到INSTALL-WIN32.txt(windows平台)

            2.打开\examples下的examples.dsw,编译链接即可完成。

            3.分析所要测试的类class

    class Money
    {
    public:
      Money( double amount, std::string currency )
        : m_amount( amount )
        , m_currency( currency )
      {
      }

      double getAmount() const
      {
        return m_amount;
      }

      std::string getCurrency() const
      {
        return m_currency;
      }

      bool ōperator ==( const Money &other ) const
      {
        return m_amount == other.m_amount  && 
               m_currency == other.m_currency;
      }

      bool operator !=( const Money &other ) const
      {
        return !(*this == other);
      }

      Money &operator +=( const Money &other )
      {
        if ( m_currency != other.m_currency )
          throw IncompatibleMoneyError();

        m_amount += other.m_amount;
        return *this;
      }

    private:
      double m_amount;
      std::string m_currency;
    };

            4. 所要测试的有哪些接口呢?

            我们分析一下这个类的公开的属性和方法。这些都是我们要测试的接口。

    构造函数
    Money( double amount, std::string currency )
    接口函数有
    double getAmount() const
    std::string getCurrency() const
    bool ōperator ==( const Money &other ) const
    bool operator !=( const Money &other ) const
    Money &operator +=( const Money &other )

    添加一个测试集test suite
     
            测试集包含一组相关的测试用例,他的目的像其他的框架中的test suite和test fixture共同的功能。
            有两个关键的方法(都是可选的)是setup和teardown,它们总是成对出现。
    struct TestAccountSuite : TestSuite
    {
        const char* name() { return "Account suite"; }
     
        void setup()
        {
            account = new Account();
        }
     
        void teardown() 
        {
            delete account;
        }
     
        Account* account;
    };

            把它们组织到一起
            一旦一个测试集和至少一个测试用例完成之后我们可以将它们放到一个runner中执行。
    #include <stdio.h>
    #include "shortcut.h"
    #include "tests/account.h"
    int main(int argc, char* argv[])
    {
        TestRunner runner;
        TestAccountSuite accountSuite;
        TestAccountWithdrawal accountWithdrawalTest;
     
        accountSuite.AddTest(&accountWithdrawalTest);
        runner.AddSuite(&accountSuite);
        runner.RunTests();
        
        return 0;
    }

            这个很小的系统的好处是能够都在一个头文件中。这就防止不同类的声明定义重复。因为之用一个头文件,所以只需要要一个驱动(driver),比如在main函数中。
    这个系统可以很容易添加一个测试用例到现有的程序中。比如,一些用例被#ifdef DEBUG宏控制块,在Release版本就不会输出到二进制文件中,不会连接到其他单元测试库上。
            显然这不是长效的解决方法,尽管是一个好的方法的开始。开发人员能够在开发时间内分离测试和code才是

    基类
            所有的测试用例派生与一个基类TestCase.
    struct TestCase
    {
        TestCase() : next(0) {}
        
        virtual void test(TestSuite* suite) {}
        virtual const char* name() { return "?"; }
        
        TestCase* next;
    };

            有个名字的方法用来记录错误的。用一个指针指向测试用例的列表。它本身是个虚函数能够派生。

            测试集test suite具有相同的结构,除了包含一系列case和两个重载函数setup和teardown

    +struct TestSuite
    {
        TestSuite() : next(0), tests(0) {}
     
        virtual void setup() {}
        virtual void teardown() {}
        virtual const char* name() { return "?"; }
     
        void AddTest(TestCase* tc)
        {
            tc->next = tests;
            tests = tc;
        }
     
        TestSuite* next;
        TestCase* tests;
    };

            像开始提及的一样这个test suite类扮演着其他框架中的 test suite和test fixture类的角色. 这在写框架中,当fixture提供setup/teardown机制时,suite常常扮演测试组织的角色,因为 ShortCUT是一个简单的框架,就不需要创建这些复杂的类了。当开发需要一个类似的功能是很容易定制的加上

    执行Runner
     
            一个测试的runner是测试的主体,当然也是一个非常直接的他的主线是调用测试的方法,run每个suite。
    struct TestRunner
    {
        ...
        void RunSuite(TestSuite* suite, int& testCount, int& passCount)
        {
            TestCase* test = suite->tests;
            while (test)
            {
                try
                {
                    suite->setup();
                    test->test(suite);
                    passCount++;
                }
                catch (TestException& te)
                {
                    log->write("FAILED '%s': %s\n", test->name(), te.text());
                }
                catch (...)
                {
                    log->write("FAILED '%s': unknown exception\n", test->name());
                }
     
                try
                {
                    suite->teardown();
                }
                catch (...)
                {
                    log->write("FAILED: teardown error in suite '%s'\n", suite->name());
                }
     
                test = test->next;
                testCount++;
            }
        }
        ...
    }

            这个关键点要注意,首先记录类执行在框架之外。这就容易让结果输出到另外一个目标中,像一个窗体。第二点,烦恼的是测试集合和测试用例像个链子一样,这就像一个LIFO的规则一样,反向与添加时候的顺序。

            这就会有一个简单的方式能够修复这个问题,但是为了框架的简单性我删除了它。


            这个框架的主要目标是是个尽量简单的解决方案。像TestLog类可以加到上面来帮助满足需求,尽管框架是简单的,但也不会失去基本的机动性。
            头文件有200行代码,四分之一是不需要的,希望能能够组建一个能够剪裁的系统、能够很容易使用修改定制的系统。

  • 用cpp做c++单元测试

    2007-08-21 23:13:17

     介绍:
            在QA中,主要有两种测试
            单元测试:验证我们系统中的所有逻辑单元的验证行为(并不考虑其他单元的相互关系,比如其他的可以打成桩函数等。)
            系统测试(集成测试)各个单元之间的相互关系,检测系统运行行为。
    单元测试用例设计
            在开发过程中,程序员通常用调试器来测试他们的程序,但是很少有人去单步调试程序,不会检测每个可能的变量值,这样我们就要借助一些工具来完成。就是我们所说的“单元测试框架”来测试我们的程序。
            我们来测试一个简单的c程序

    BOOL addition(int a, int b)
    {
        return (a + b);
    }
            我们的用例必须借助其他的c函数来完成验证所有的可能性,返回True或者False来说明测试是否通过
    BOOL additionTest()
    {
        if ( addition(1, 2) != 3 )
            return (FALSE);

        if ( addition(0, 0) != 0 )
            return (FALSE);

        if ( addition(10, 0) != 10 )
            return (FALSE);

        if ( addition(-8, 0) != -8 )
            return (FALSE);

        if ( addition(5, -5) != 0 )
            return (FALSE);

        if ( addition(-5, 2) != -3 )
            return (FALSE);

        if ( addition(-4, -1) != -5 )
            return (FALSE);

        return (TRUE);
    }
            我们看到,测试所有的可能性需要
            正数+负数, 0+0, 负数+0, 正数+0,正数+正数,负数+正数,负数+负数
            每个cases比较了加的结果和期望值,如果不通过就False,如果都通过就返回True

            行为上可以设计下面的例子:
    int additionPropertiesTest()
    {
        // conmutative: a + b = b + a
        if ( addition(1, 2) != addition(2, 1) )
            return (FALSE);

        // asociative: a + (b + c) = (a + b) + c
        if ( addition(1, addition(2, 3)) != addition(addition(1, 2), 3) )
            return (FALSE);

        // neutral element: a + NEUTRAL = a
        if ( addition(10, 0) != 10 )
            return (FALSE);

        // inverse element: a + INVERSE = NEUTRAL
        if ( addition(10, -10) != 0 )
            return (FALSE);

        return (TRUE);
    }

            但是这样当代码变化时用例就得跟着相应的变化,或者去加一个新的case
            XP(极限编程)推荐就是在编写代码之前先写测试用例。就是测试驱动开发。

    CPPUnit

    CPPUnit
            各Case应该被写在类里面从TestCase 导出。这个类对我们所有基本功能进行测试, 在Test Suite(测试用例集合)登记等等

    例如, 我们写了一个功能在磁盘存放一些数据的小模块。 这个模块(类名DiskData) 有主要二功能: 装载和保存数据到文件里面:
    typedef struct _DATA
    {
        int  number;
        char string[256];
    } DATA, *LPDATA;


    class DiskData
    {
    public:
        DiskData();
        ~DiskData();

        LPDATA getData();
        void setData(LPDATA value);

        bool load(char *filename);
        bool store(char *filename);

    private:
        DATA m_data;
    };

            现在, 什么编码方式并不重要, 因为最重要事是我们必须肯定它必须做, 是这个类应该做: 正确地装载和存放数据到文件。

            为了做这个验证,我们去创造一个新的测试集,包括二个测试用例: 一个装载数据和另为存储数据。

    使用 CPPUnit

            你能在这里http://cppunit.sourceforge.net/得到最新的CPPUnit 版本, 你能发现所有的库 , 文献, 例子和其它有趣的材料。(我下载了版本为1.8.0 并且这个颁布工作良好)

            在Win32里, 你能在VC++ 之下(6.0 和以后版本)使用CPPUnit , 但是当CPPUnit 使用ANSI C++, 有少量接口时针对其它环境象C++Builder。

            在CPPUnit发布版本里面,所有建造库的步骤和信息,可以在INSTALL-WIN32.txt文件找到,。当所有二进制文件被构建之后, 你就能写你自己的测试集了。

            想在VC中写自己的测试程序,可以按照以下步骤:
            建立一个MFC的对话框(或文档视图结构)
            允许时间类型信息,Alt+F7 --> C/C++ --> C++ language --> Enable RTTI
            把Cppunit\inlude放到include目录:Tools - Options - Directories - Include.
            用cppunitd.lib (静态连接) 或者cppunitd_dll.lib (动态链接),testrunnerd.lib来链接你的程序。
            如果动态链接,就要把testrunnerd.dll 拷到应用程序目录来运行。

            Ok,看一下测试用例的类的定义吧。

    #if !defined(DISKDATA_TESTCASE_H_INCLUDED)
    #define DISKDATA_TESTCASE_H_INCLUDED

    #if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000

    #include <cppunit/TestCase.h>
    #include <cppunit/extensions/HelperMacros.h>

    #include "DiskData.h"

    class DiskDataTestCase : public CppUnit::TestCase
    {
      CPPUNIT_TEST_SUITE(DiskDataTestCase);
          CPPUNIT_TEST(loadTest);
          CPPUNIT_TEST(storeTest);
      CPPUNIT_TEST_SUITE_END();

    public:
        void setUp();
        void tearDown();

    protected:
        void loadTest();
        void storeTest();

    private:
        DiskData *fixture;   
    };

    #endif
      首先, 必须包含TestCase.h和HelperMacros.h. 第一步,我们的从我们的Testcase基类配生的新类。第二,用一些宏使我们的定义的更方便,如 CPPUNIT_TEST_SUITE (开始测试定义), CPPUNIT_TEST (定义一个测试用例) 或 CPPUNIT_TEST_SUITE_END (结束一个测试集).

            我们的类(DiskDataTestCase)有重载了两个方法setUp()和tearDown(). 一个开始,一个结束测试。

            测试过程如下

            启动程序
            点击“Run”
            调用Call setUp()方法: 构建我们的测试对象fixture  
            调用第一个测试方法
            调用tearDown() 方法,清除对象
            调用Call setUp()方法: 构建我们的测试对象fixture  
            调用第一个测试方法
            调用Call setUp()方法: 构建我们的测试对象fixture
    ... 
            就像下面的形式:

    #include "DiskDataTestCase.h"

    CPPUNIT_TEST_SUITE_REGISTRATION(DiskDataTestCase);


    void DiskDataTestCase::setUp()
    {
        fixture = new DiskData();
    }

    void DiskDataTestCase::tearDown()
    {
        delete fixture;
        fixture = NULL;
    }


    void DiskDataTestCase::loadTest()
    {
        // our load test logic
    }


    void DiskDataTestCase::storeTest()
    {
        // our store test logic
    }


    编写测试用例
            一旦我们知道我们要测什么之后,我们就可以写测试用例了。我们能够执行所有的我们需要的操作:使用普通库函数,第三方库,win32api库函数,或简单使用c++内部操作

            有时候,我们需要调用外部辅助文件或者数据库,比较外部文件和内部数据是否一致。
            每发现一个错误时9比如发现内部数据和外部数据不同我们就创建一个异常,使用 CPPUNIT_FAIL(message) 来显示异常信息。

            检测一个条件就使用
            CPPUNIT_ASSERT(condition):如果为false就抛出异常
            CPPUNIT_ASSERT_MESSAGE(message, condition): 如果为false就抛出制定的信息。
            CPPUNIT_ASSERT_EQUAL(expected,current): 检测期望值 
            CPPUNIT_ASSERT_EQUAL_MESSAGE(message,expected,current): 当比较值不相等时候抛出的制定的信息。
            CPPUNIT_ASSERT_DOUBLES_EQUAL(expected,current,delta): 带精度的比较 
            下面是测试loadTest的例子,
    //
    // These are correct values stored in auxiliar file
    //
    #define AUX_FILENAME    "ok_data.dat"
    #define FILE_NUMBER    19
    #define FILE_STRING    "this is correct text stored in auxiliar file"

    void DiskDataTestCase::loadTest()
    {
        // convert from relative to absolute path
        TCHAR    absoluteFilename[MAX_PATH];
        DWORD    size = MAX_PATH;

        strcpy(absoluteFilename, AUX_FILENAME);
        CPPUNIT_ASSERT( RelativeToAbsolutePath(absoluteFilename, &size) );

        // executes action
        CPPUNIT_ASSERT( fixture->load(absoluteFilename) );

        // ...and check results with assertions
        LPDATA    loadedData = fixture->getData();

        CPPUNIT_ASSERT(loadedData != NULL);
        CPPUNIT_ASSERT_EQUAL(FILE_NUMBER, loadedData->number);
        CPPUNIT_ASSERT( 0 == strcmp(FILE_STRING,
                fixture->getData()->string) );
    }

       在这个case我们得到四个可能的错误:
    load method's return value
    getData method's return value
    number structure member's value
    string structure member's value

            第二个用例也是相似的。但是困难点,我们需要使用已知的数据来填充fixture,把它存在磁盘临时文件里,然后打开两个文件(新的和辅助文件),读并比较内容,两者如一致就正确

    void DiskDataTestCase::storeTest()
    {
        DATA    d;
        DWORD      tmpSize, auxSize;
        BYTE     *tmpBuff, *auxBuff;
        TCHAR    absoluteFilename[MAX_PATH];
        DWORD    size = MAX_PATH;

        // configures structure with known data
        d.number = FILE_NUMBER;
        strcpy(d.string, FILE_STRING);

        // convert from relative to absolute path

        strcpy(absoluteFilename, AUX_FILENAME);
        CPPUNIT_ASSERT( RelativeToAbsolutePath(absoluteFilename, &size) );

        // executes action
        fixture->setData(&d);
        CPPUNIT_ASSERT( fixture->store("data.tmp") );

        // Read both files contents and check results
        // ReadAllFileInMemory is an auxiliar function which allocates a buffer
        // and save all file content inside it. Caller should release the buffer.
        tmpSize = ReadAllFileInMemory("data.tmp", tmpBuff);
        auxSize = ReadAllFileInMemory(absoluteFilename, auxBuff);

        // files must exist
        CPPUNIT_ASSERT_MESSAGE("New file doesn't exists?", tmpSize > 0);
        CPPUNIT_ASSERT_MESSAGE("Aux file doesn't exists?", auxSize > 0);

        // sizes must be valid
        CPPUNIT_ASSERT(tmpSize != 0xFFFFFFFF);
        CPPUNIT_ASSERT(auxSize != 0xFFFFFFFF);

        // buffers must be valid
        CPPUNIT_ASSERT(tmpBuff != NULL);
        CPPUNIT_ASSERT(auxBuff != NULL);

        // both file's sizes must be the same as DATA's size
        CPPUNIT_ASSERT_EQUAL((DWORD) sizeof(DATA), tmpSize);
        CPPUNIT_ASSERT_EQUAL(auxSize, tmpSize);

        // both files content must be the same
        CPPUNIT_ASSERT( 0 == memcmp(tmpBuff, auxBuff, sizeof(DATA)) );

        delete [] tmpBuff;
        delete [] auxBuff;

        ::DeleteFile("data.tmp");
    }


    调用用户接口
            最后,我们看看用一个mfc 对话框(TestRunner.dll)用来说明。

            我们需要在我们的初始化函数中做如下初始化

    #include <cppunit/ui/mfc/TestRunner.h>
    #include <cppunit/extensions/TestFactoryRegistry.h>

    BOOL CMy_TestsApp::InitInstance()
    {
        ....

        // declare a test runner, fill it with our registered tests and run them
        CppUnit::MfcUi::TestRunner runner;

        runner.addTest( CppUnit::TestFactoryRegistry::getRegistry().makeTest() );

        runner.run();   

        return TRUE;
    }
            只要定义一个test的实例,然后注册所有用例,在跑case。

Open Toolbar