• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
    • 軟件測試技術
    • 軟件測試博客
    • 軟件測試視頻
    • 開源軟件測試技術
    • 軟件測試論壇
    • 軟件測試沙龍
    • 軟件測試資料下載
    • 軟件測試雜志
    • 軟件測試人才招聘
      暫時沒有公告

    字號: | 推薦給好友 上一篇 | 下一篇

    用cpp做C單元測試

    發布: 2008-6-18 17:45 | 作者: webmaster | 來源: CSDN | 查看: 89次 | 進入軟件測試論壇討論

    領測軟件測試網 介紹:
      在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。

    延伸閱讀

    文章來源于領測軟件測試網 http://www.kjueaiud.com/

    TAG: 單元 cpp


    關于領測軟件測試網 | 領測軟件測試網合作伙伴 | 廣告服務 | 投稿指南 | 聯系我們 | 網站地圖 | 友情鏈接
    版權所有(C) 2003-2010 TestAge(領測軟件測試網)|領測國際科技(北京)有限公司|軟件測試工程師培訓網 All Rights Reserved
    北京市海淀區中關村南大街9號北京理工科技大廈1402室 京ICP備2023014753號-2
    技術支持和業務聯系:info@testage.com.cn 電話:010-51297073

    軟件測試 | 領測國際ISTQBISTQB官網TMMiTMMi認證國際軟件測試工程師認證領測軟件測試網

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>