Wings-企業(yè)級(jí)單元測(cè)試自動(dòng)編碼引擎白皮書

第一章 Wings企業(yè)級(jí)單元測(cè)試自動(dòng)編碼引擎誕生的背景

附Wings免費(fèi)下載和白皮書pdf下載


隨著科技的飛速發(fā)展,軟件系統(tǒng)越來(lái)越復(fù)雜,在系統(tǒng)測(cè)試階段不斷遇到的瓶頸,迫使行業(yè)逐步追根溯源到了單元測(cè)試階段。軟件缺陷發(fā)現(xiàn)得越晚,其處理費(fèi)用就越呈幾何激增,因此測(cè)試左移概念已經(jīng)成為趨勢(shì)。

單元測(cè)試面臨的最大問(wèn)題是單元測(cè)試用例編寫工作量巨大,極端情況下與開發(fā)工作量比達(dá)到1:1,甚至更高,這使大部分開發(fā)團(tuán)隊(duì)要么主動(dòng)忽視單元測(cè)試,要么象征性的走個(gè)流程。

如果可以讓計(jì)算機(jī)先對(duì)被測(cè)試程序進(jìn)行全局分析和深度理解,再由計(jì)算機(jī)進(jìn)行全自動(dòng)的完成單元測(cè)試編碼,同時(shí)還能確保自動(dòng)編寫的代碼無(wú)語(yǔ)法、語(yǔ)義錯(cuò)誤的直接運(yùn)行起來(lái),這種用計(jì)算機(jī)智能算法全自動(dòng)產(chǎn)生的測(cè)試編碼去驗(yàn)證開發(fā)人員編寫的源代碼邏輯輸入輸出對(duì)錯(cuò)的高端測(cè)試模式,無(wú)疑是未來(lái)軟件測(cè)試領(lǐng)域最為璀璨的“明珠”技術(shù)。

國(guó)外軟件諸如c++ test完成了這個(gè)領(lǐng)域的初步技術(shù)探索,星云測(cè)試研發(fā)的Wings(目前商用產(chǎn)品支持c/c++程序)產(chǎn)品,則大踏步完成了整體技術(shù)跨越和多方商用落地驗(yàn)證。

Wings可以對(duì)程序參數(shù)進(jìn)行深度解析,比如c++類、模板類、數(shù)組、結(jié)構(gòu)體、指針、鏈表以及任意復(fù)雜結(jié)構(gòu)的層級(jí)嵌套,同時(shí)對(duì)于面向?qū)ο蟮某绦蛱匦砸约俺S玫娜萜鲙?kù),能夠完美識(shí)別和支持。對(duì)于一些void*、函數(shù)指針、模板類等無(wú)法直接靜態(tài)分析進(jìn)行類型確定的特殊情況,均有基于人工智能的程序分析輔助進(jìn)行類型確定。

Wings在基于深度參數(shù)解析的基礎(chǔ)上,對(duì)于全局范圍的程序進(jìn)行理解分析后,第一步 按照內(nèi)置規(guī)則,自動(dòng)化構(gòu)建被測(cè)程序的輸入用例代碼;第二步 構(gòu)建測(cè)試代碼用于調(diào)用被測(cè)程序的源代碼;第三步 構(gòu)建被測(cè)程序輸出斷言,完成調(diào)用被測(cè)試程序單元的全部環(huán)境準(zhǔn)備。這個(gè)構(gòu)建速度非???,可以達(dá)到每分鐘100萬(wàn)行左右的生成速度,編寫的代碼比程序開發(fā)人員手工編寫的規(guī)范度高出一截,并確保100% 的語(yǔ)法語(yǔ)義正確,免去大量的調(diào)試時(shí)間。

在驅(qū)動(dòng)數(shù)據(jù)上,Wings實(shí)現(xiàn)了驅(qū)動(dòng)代碼和數(shù)據(jù)的分離。Wings基于深度參數(shù)解析基礎(chǔ)上,可以根據(jù)參數(shù)的結(jié)構(gòu)自動(dòng)生成層級(jí)嵌套的測(cè)試數(shù)據(jù)結(jié)構(gòu),用圖形界面可視化的展示給用戶。用戶只需要根據(jù)Wings提供的界面向?qū)?duì)測(cè)試數(shù)據(jù)進(jìn)行填充即可,驅(qū)動(dòng)程序會(huì)自動(dòng)識(shí)別并讀取這些數(shù)據(jù),完成對(duì)被測(cè)試程序的調(diào)用。

Wings還可以全自動(dòng)生成參數(shù)捕獲程序,并自動(dòng)插裝在被測(cè)試程序中。當(dāng)被測(cè)試程序運(yùn)行后,可以通過(guò)專用軟件捕獲程序中每個(gè)函數(shù)模塊運(yùn)行的具體參數(shù)值。Wings的測(cè)試代碼驅(qū)動(dòng)自動(dòng)生成和參數(shù)捕獲,相當(dāng)于完成了一種全智能的閉環(huán)測(cè)試驗(yàn)證體系。Wings使測(cè)試數(shù)據(jù)不需要人工準(zhǔn)備,只需要在前序輪次中通過(guò)參數(shù)捕獲自動(dòng)存儲(chǔ)。若前序測(cè)試用例運(yùn)行正常,那么這些數(shù)據(jù)都可以作為后續(xù)測(cè)試輸入和進(jìn)行校驗(yàn)的基礎(chǔ)數(shù)據(jù)。

第二章 單元測(cè)試自動(dòng)生成技術(shù)

2.1 測(cè)試左移后單元測(cè)試面臨的問(wèn)題

測(cè)試左移后,傳統(tǒng)的單元測(cè)試一般面臨很多問(wèn)題,主要如下:

(1) 傳統(tǒng)程序級(jí)測(cè)試用例的編寫會(huì)耗費(fèi)開發(fā)人員大量的工時(shí),比如TDD測(cè)試驅(qū)動(dòng)開發(fā)里面的單元測(cè)試無(wú)法有效實(shí)施,導(dǎo)致所有測(cè)試幾乎全部依賴于系統(tǒng)級(jí)黑盒測(cè)試。程序級(jí)測(cè)試用例的開發(fā)成本是功能實(shí)現(xiàn)代碼本身時(shí)間至少為1:1,絕大部分企業(yè)選擇放棄開發(fā)程序級(jí)測(cè)試,而采用系統(tǒng)級(jí)測(cè)試方法。

(2) 需求發(fā)生變化導(dǎo)致程序?qū)崿F(xiàn)發(fā)生變化后,程序集用例也需要發(fā)生變化,和自動(dòng)化面臨的問(wèn)題一樣,單元測(cè)試本身的可維護(hù)性問(wèn)題導(dǎo)致投入是持續(xù)的而不是一次性的,會(huì)打消其企業(yè)應(yīng)用的熱情。

(3) 很多單元在未組裝成系統(tǒng)的時(shí)候切入,如果需要進(jìn)行測(cè)試需要進(jìn)行大量的mock操作或者樁模擬,這個(gè)過(guò)程會(huì)造成單元測(cè)試的不精確性。

(4) 程序集測(cè)試數(shù)據(jù)量很大,全部需要用戶來(lái)進(jìn)行準(zhǔn)備無(wú)法全自動(dòng)從前序系統(tǒng)測(cè)試過(guò)程中獲取。

針對(duì)以上問(wèn)題,很多業(yè)內(nèi)人士提出了很多辦法,例如自動(dòng)生成測(cè)試用例、自動(dòng)構(gòu)建測(cè)試驅(qū)動(dòng)、模糊測(cè)試方法等諸多方式,但是實(shí)際開發(fā)中程序輸入?yún)?shù)十分復(fù)雜,簡(jiǎn)單的輸入已經(jīng)不能滿足,如何能夠構(gòu)建復(fù)雜的參數(shù)輸入,是要解決的重要問(wèn)題。因此,星云測(cè)試研發(fā)了Wings產(chǎn)品--單元級(jí)的自動(dòng)編碼引擎,它不僅能夠自動(dòng)構(gòu)建測(cè)試驅(qū)動(dòng),還能處理十分復(fù)雜的大型程序參數(shù),幫助企業(yè)能夠更好的進(jìn)行單元測(cè)試。

2.2 完成單元測(cè)試要素

構(gòu)成單元測(cè)試的要素如下:

a. 測(cè)試數(shù)據(jù)

b. 測(cè)試驅(qū)動(dòng)代碼

例如針對(duì)以下程序,需要準(zhǔn)備的測(cè)試輸入以及測(cè)試代碼

① 全局變量:c

② 參數(shù):a、b

③ 預(yù)期輸出:320 30

int c = 100;
int sum(int a, int b)
{
    return a + b + c;
}
int driver_sum()
{
    int a = 100, b = 20;
    c = 200;
    return sum(a, b);
}
TEST(test, driver_sum)
{
    EXPECT_EQ(320, driver_sum);
    EXPECT_EQ(30, driver_sum);
}

2.3構(gòu)建測(cè)試數(shù)據(jù)和測(cè)試代碼遇到的技術(shù)瓶頸

編寫測(cè)試代碼比較繁瑣

開發(fā)編寫驅(qū)動(dòng)不難,但是浪費(fèi)大量的時(shí)間,并且沒有創(chuàng)造性。

編寫代碼是否有良好的代碼規(guī)范

單元測(cè)試也需要一些規(guī)范支持,例如代碼規(guī)范,注釋規(guī)范等,有了這些規(guī)范能夠?yàn)闇y(cè)試人員提供很好的功能測(cè)試用例設(shè)計(jì)的邏輯思考,也為其他開發(fā)熟悉代碼提供極大的便利。

數(shù)據(jù)類型比較復(fù)雜

通常情況下,輸入?yún)?shù)比較復(fù)雜,嵌套層析結(jié)構(gòu)比較深入,例如類包含其他類等。

能否支持多組測(cè)試數(shù)據(jù)

一般代碼中每個(gè)循環(huán)、分支判斷以及輸入輸出都有可能產(chǎn)生缺陷。因此單元測(cè)試需要很多用例,滿足不同的條件分支,達(dá)到滿足覆蓋率。

第三章 Wings的基本架構(gòu)介紹

3.1 Wings測(cè)試用例驅(qū)動(dòng)自動(dòng)生成技術(shù)的特性

(1)Wings是智能的全自動(dòng)測(cè)試用例驅(qū)動(dòng)構(gòu)建系統(tǒng),能夠在很短時(shí)間內(nèi)完成對(duì)大型復(fù)雜程序的自動(dòng)解析、構(gòu)建。每分鐘達(dá)到100萬(wàn)行代碼的生成速率。

(2)可以將任意復(fù)雜類型逐步分解為基本數(shù)據(jù)類型,例如結(jié)構(gòu)體嵌套,復(fù)雜類對(duì)象,c++標(biāo)準(zhǔn)容器,自定義模板類等。

(3)支持多層次的可視化的數(shù)據(jù)表格來(lái)對(duì)變量類型進(jìn)行賦值,無(wú)需關(guān)注驅(qū)動(dòng)程序本身。數(shù)據(jù)表格可以表達(dá)任意深度和多層次的數(shù)據(jù)關(guān)系,用戶只需要關(guān)注表格數(shù)據(jù),完成對(duì)輸入數(shù)據(jù)的校對(duì)。

(4)能夠區(qū)分系統(tǒng)數(shù)據(jù)類型和用戶自定義類型,對(duì)于復(fù)雜的約定俗成系統(tǒng)類型可由用戶自定義扁平式賦值模板,例如std::vector類型等,內(nèi)部集成常用系統(tǒng)類型的模板。

3.2 Wings的技術(shù)架構(gòu)介紹

Wings的技術(shù)架構(gòu):首先Wings利用代碼靜態(tài)分析技術(shù),提取被測(cè)程序的主干信息并保存到Program Structure Description(PSD)結(jié)構(gòu)中,PSD結(jié)構(gòu)中主要存儲(chǔ)函數(shù)的參數(shù)、全局變量以及返回值的信息,類的成員變量,成員函數(shù),以及訪問(wèn)權(quán)限等信息。利用存儲(chǔ)的信息,依據(jù)一定的編寫規(guī)則,自動(dòng)構(gòu)建測(cè)試驅(qū)動(dòng)、googletest 期望斷言、測(cè)試輸入、參數(shù)捕獲等代碼和值。

圖 3.2 Wings*總體架構(gòu)圖

上述Wings的總體架構(gòu)圖,說(shuō)明了Wings構(gòu)建代碼與測(cè)試輸入的具體過(guò)程,整個(gè)核心技術(shù)是通過(guò)編譯底層技術(shù)獲取程序的信息,然后按照一定的規(guī)則構(gòu)建需要的數(shù)據(jù)。

第四章 程序結(jié)構(gòu)信息描述

程序結(jié)構(gòu)信息(Program Structure Description)是指對(duì)源程序進(jìn)行提取后的描述信息,主要包括類名、命名空間、類的成員變量信息、類的函數(shù)信息,以及各種類型信息等(程序結(jié)構(gòu)信息,下文簡(jiǎn)稱“PSD”)。描述信息的保存C語(yǔ)言是以一個(gè)文件為單元存儲(chǔ),C++提取所有的類信息存儲(chǔ)在一個(gè)文件中。

Wings的主要特性是能夠?qū)?fù)雜的類型(結(jié)構(gòu)體、類、模板類等)進(jìn)行逐層展開

分解到最基本數(shù)據(jù)類型(char、int、string等)。

PSD結(jié)構(gòu)存儲(chǔ)在XML文件中,不同的XML文件存儲(chǔ)不同的描述信息。Wings提取的程序結(jié)果都存儲(chǔ)在Wingsprojects文件下的funxml與globalxml文件夾中,其中funxml文件中主要存儲(chǔ)的文件以及作用如下:

RecordDecl.xml: 結(jié)構(gòu)體,聯(lián)合體與類描述信息。

ClassTemplateDecl.xml:模板類描述信息

EnumDecl.xml:枚舉描述信息

funcPoint.txt: 存儲(chǔ)函數(shù)參數(shù)為函數(shù)指針的分析結(jié)果。

funcCount.txt: 存儲(chǔ)分析到的全部函數(shù),參數(shù)個(gè)數(shù),參數(shù)類型信息。

void.txt: 存儲(chǔ)函數(shù)參數(shù)為void*的分析結(jié)果。

filename.xml:存儲(chǔ)c語(yǔ)言文件的信息。

注:具體文件的描述信息,參看附錄A。

針對(duì)復(fù)雜類型,例如結(jié)構(gòu)體類型location_s,成員變量中除了基本數(shù)據(jù)類型之外,還存在包含結(jié)構(gòu)體類型的情況,如下所示的代碼中,location_s中包含coordinate_s結(jié)構(gòu)體,以及FILE等類型的信息,針對(duì)不同的類型進(jìn)行標(biāo)記區(qū)分。

源代碼如下:


typedef struct coordinate_s{
    void(*setx)(double, int);
    int mInt;
    char *mPoi;
    int mArr[2][3];
    void *vo;
} coordinate;

typedef struct location_s {
    int **mPoi;
    coordinate *coor;
    FILE *pf;
    struct location_s *next;
}location;

coordinate *coordinate_create(void);
int coordinate_destroy(location *loc,size_t length,char *ch);
void func_point(double param1, int param2);

生成對(duì)應(yīng)的PSD結(jié)構(gòu)信息如下圖4:

圖4:PSD描述信息

C++的主要表示類型是類,因此測(cè)試是C++以一個(gè)類為單元做測(cè)試,類主要包括類的成員變量名以及類型信息,成員變量的訪問(wèn)權(quán)限信息。類的成員函數(shù)分為構(gòu)造函數(shù)、內(nèi)聯(lián)函數(shù)、虛函數(shù)等,成員函數(shù)的參數(shù)信息以及類型信息等。具體描述信息可以登陸www.codeWings.net下載Wings試用版本進(jìn)行學(xué)習(xí)。

第五章 Wings構(gòu)建程序描述

Wings通過(guò)獲取到的存儲(chǔ)信息,依據(jù)一定的編碼規(guī)則,構(gòu)建不同的代碼程序,如驅(qū)動(dòng)程序、期望斷言程序、參數(shù)捕獲程序等。

5.1 驅(qū)動(dòng)程序構(gòu)建

驅(qū)動(dòng)程序指單元測(cè)試代碼,Wings針對(duì)函數(shù)參數(shù)的類型,自動(dòng)完成單元測(cè)試代碼的編寫。為了更詳細(xì)的說(shuō)明驅(qū)動(dòng)程序,以一個(gè)C++類作為具體的例子說(shuō)明。

類的聲明如下:

class BlockBuilder {
public:
    explicit BlockBuilder(const Options* options);
    BlockBuilder(const BlockBuilder&) = delete;
    BlockBuilder& operator=(const BlockBuilder&) = delete;
    void Reset();
    void Add(const Slice& key, std::string & value);
    Slice Finish();
    size_t CurrentSizeEstimate() const;
private:
    const Options* options_;
    std::string buffer_;
    int counter_;
    bool finished_;
};

針對(duì)如上BlockBuilder 類,構(gòu)建一個(gè)對(duì)應(yīng)的驅(qū)動(dòng)類DriverBlockBuilder,驅(qū)動(dòng)類中主要包含構(gòu)造函數(shù)、析構(gòu)函數(shù)、每個(gè)成員函數(shù)的驅(qū)動(dòng)函數(shù)以及返回值函數(shù)。為了避免類中重名函數(shù),對(duì)寫入PSD需要對(duì)應(yīng)生成驅(qū)動(dòng)的函數(shù)進(jìn)行順序編號(hào)。

驅(qū)動(dòng)類聲明:

class DriverBlockBuilder {
public:
    DriverBlockBuilder(Json::Value Root, int times);
    ~DriverBlockBuilder();
    int DriverBlockBuilderReset1(int times);
    int Reset1Times;
    int DriverBlockBuilderAdd2(int times);
    int Add2Times;
    int DriverBlockBuilderFinish3(int times);
    void ReturnDriver_Finish3(class Wings::Slice returnType);
    int Finish3Times;
    int DriverBlockBuilderCurrentSizeEstimate4(int times);
    void ReturnDriver_CurrentSizeEstimate4(size_t returnType);
    int CurrentSizeEstimate4Times;
private:
    Wings::BlockBuilder* _BlockBuilder;
};

在上述驅(qū)動(dòng)類中,構(gòu)造函數(shù)的作用是構(gòu)造BlockBuilder的對(duì)象,用構(gòu)造的對(duì)象,調(diào)用測(cè)試BlockBuilder中的成員函數(shù)。析構(gòu)函數(shù)的作用是釋放構(gòu)建的對(duì)象。

構(gòu)造函數(shù)與析構(gòu)函數(shù):

DriverBlockBuilder::DriverBlockBuilder(Json::Value Root, int times){
  Json::Value _BlockBuilder_Root = Root["BlockBuilder" + std::to_string(times)];
  /* options_ */
  Json::Value _options__Root = _BlockBuilder_Root["options_"];
  int _options__len = _options__Root.size();
  Wings::Options* _options_ = DriverstructOptionsPoint(_options__Root, _options__len);
  /* buffer_ */
  std::string _buffer_= _BlockBuilder_Root["buffer_"].asString();
  /* counter_ */
  int _counter_ = _BlockBuilder_Root["counter_"].asInt();
  /* finished_ */
  bool _finished_;
  int _finished__value_ = _BlockBuilder_Root["finished_"].asInt();
  if (_finished__value_ == 0) {
    _finished_ = true;
   }
  else {
    _finished_ = false;
  }
  _BlockBuilder = new Wings::BlockBuilder(_options_, _buffer_, _counter_, _finished_, false);
}
DriverBlockBuilder::~DriverBlockBuilder()
{
    if (_BlockBuilder != nullptr) {
        delete _BlockBuilder;
}
}

每個(gè)成員函數(shù)對(duì)應(yīng)生成自己的驅(qū)動(dòng)函數(shù)。類中的Add函數(shù)對(duì)應(yīng)的驅(qū)動(dòng)函數(shù)如下。

Add驅(qū)動(dòng)函數(shù):

int DriverBlockBuilder::DriverBlockBuilderAdd2(int times)
{
    Add2Times = times;
    const char* jsonFilePath = "drivervalue/BlockBuilder/Add2.json";
    Json::Value Root;
    Json::Reader _reader;
    std::ifstream _ifs(jsonFilePath);
    _reader.parse(_ifs, Root);
    Json::Value _Add2_Root = Root["Add2" + std::to_string(times)];
    /*It is the 1 global variable: count    Add */
    int _count = _Add2_Root["count"].asInt();
    count = _count;
    /*It is the 1 parameter: key Add2
     * Parameters of the prototype:const Wings::Slice &key */
    Json::Value _keykey_Root = _Add2_Root["key"];
    /* data_ */
    char* _keydata_;
    {
        std::string _keydata__str = _keykey_Root["data_"].asString();
        _keydata_ = new char[_keydata__str.size()];
        memcpy(_keydata_, _keydata__str.c_str(), _keydata__str.size());
    }
    /* size_ */
    unsigned int _keysize_ = _keykey_Root["size_"].asUInt();
    Wings::Slice _key(_keydata_, _keysize_, false);
    /*It is the 2 parameter: value    Add2
     * Parameters of the prototype:const std::string &value  */
    string _value = _Add2_Root["value"].asString();

    //The Function of Class    Call
    _BlockBuilder->Add(_key, _value);
    return 0;
}

構(gòu)成以上驅(qū)動(dòng)函數(shù)的主要包括全局變量、參數(shù)、調(diào)用被測(cè)函數(shù)的構(gòu)建。

以上是針對(duì)BlockBuilder類的驅(qū)動(dòng)類的主要信息,而構(gòu)建的程序遵守google的編碼規(guī)范。一些命名規(guī)則如下:

Wings生成的驅(qū)動(dòng)代碼,存儲(chǔ)在drivercode文件夾中。

(1)driver.cc與driver.h,針對(duì)程序中使用到的一些公共函數(shù)以及頭文件。

(2)同一個(gè)結(jié)構(gòu)體或者聯(lián)合體,可能會(huì)作為多個(gè)函數(shù)的參數(shù)使用,為了避免代碼的重復(fù),Wings針對(duì)所有的結(jié)構(gòu)體和聯(lián)合體的不同類型,封裝成不同的驅(qū)動(dòng)函數(shù)或者參數(shù)捕獲函數(shù)。driver_structorunion.cc 存儲(chǔ)結(jié)構(gòu)體驅(qū)動(dòng)函數(shù)的代driver_structorunion.h 對(duì)應(yīng)的頭文件。

結(jié)構(gòu)體實(shí)現(xiàn)函數(shù)的命名規(guī)則為:DriverStruct+結(jié)構(gòu)體名字+類型,其中Point代表一級(jí)指針或者一維數(shù)組,PointPoint代表二級(jí)指針或者二維數(shù)組使用。

源文件的命名規(guī)則為:driver_+源文件名/類名+.cc

例如:driver_nginx.cc 或 driverBlockBuilder.cc

驅(qū)動(dòng)函數(shù)的命名規(guī)則:Driver_+函數(shù)名+(編號(hào))

例如:Driver_ngx_show_version_info(void);

DriverBlockBuilderAdd2(int times)

(3)返回值的打印輸出

返回值的打印輸出函數(shù)命名規(guī)則:Driver+Return+Print_+函數(shù)名。

例如:DriverReturnPrint_ngx_show_version_info();

(4)用戶源代碼中的main函數(shù)自動(dòng)進(jìn)行注釋,重新生成一個(gè)main函數(shù)文件,來(lái)進(jìn)行測(cè)試。Wings會(huì)生成驅(qū)動(dòng)main的主函數(shù)文件為:gtest_auto_main.cc

Wings主要針對(duì)參數(shù)進(jìn)行逐層展開,解析到最底層為基本類型進(jìn)行處理。驅(qū)動(dòng)的賦值部分,主要就是針對(duì)基本類型進(jìn)行處理。(注:特殊類型,比如FILE等,后面會(huì)詳細(xì)講解如何賦值)

以int類型舉例,一般程序構(gòu)成int的主要組成大概包括以下情況:

int p; int *p; int **p; int ***p;

int p[1]; int p[2][3]; int p[1][2][3];

int(p)[]; int(p)[][3]; int (p)[]; int (**p)[];

int *a[]; int **a[]; int a[][3]; int (a[])[];

Wings會(huì)針對(duì)基本類型的以上15種類型,進(jìn)行不同的賦值。

構(gòu)建完驅(qū)動(dòng)程序之后,要對(duì)驅(qū)動(dòng)程序進(jìn)行運(yùn)行,Wings構(gòu)建googletest的框架,來(lái)進(jìn)行測(cè)試。下面我們將針對(duì)期望斷言的googletest程序進(jìn)行構(gòu)建。

5.2 googletest程序的構(gòu)建

在構(gòu)建完單元測(cè)試驅(qū)動(dòng)程序之后,Wings將調(diào)用googletest的框架,完成對(duì)返回值的期望值驗(yàn)證代碼,并且輸出測(cè)試結(jié)果。

Wings運(yùn)行單元測(cè)試過(guò)程中,會(huì)構(gòu)建返回值的保存代碼,將程序的返回值結(jié)果進(jìn)行存儲(chǔ),然后讀取用戶輸入的預(yù)期值,進(jìn)行結(jié)果對(duì)比,判斷是否通過(guò)。

針對(duì)具體的類,對(duì)應(yīng)生成gtest類,每個(gè)gtest類中對(duì)每個(gè)函數(shù)構(gòu)建期望代碼。例如針對(duì)BlockBuilder類,生成的gtest類為 GtestBlockBuilder。

GtestBlockBuilder類的聲明:

class GtestBlockBuilder : public testing::Test {
protected:
    virtual void SetUp()
    {
        const char* jsonFilePath = "../drivervalue/RecordDecl.json";
        Json::Value Root;
        Json::Reader _reader;
        std::ifstream _ifs(jsonFilePath);
        _reader.parse(_ifs, Root);
        driverBlockBuilder = new DriverBlockBuilder(Root, 0);
    }
    virtual void TearDown()
    {
        delete driverBlockBuilder;
    }

    DriverBlockBuilder* driverBlockBuilder;
};

BlockBuilder類中的每個(gè)函數(shù)對(duì)應(yīng)一個(gè)gtest函數(shù),每個(gè)函數(shù)負(fù)責(zé)調(diào)用DriverBlockBuilder編寫的驅(qū)動(dòng)函數(shù),對(duì)于包含返回值信息的函數(shù),則對(duì)應(yīng)生成具體的期望值對(duì)比。

期望對(duì)比函數(shù)CurrentSizeEstimate:

TEST_F(GtestBlockBuilder, DriverBlockBuilderCurrentSizeEstimate4)
{
    const char* jsonFilePath = "drivervalue/BlockBuilder/CurrentSizeEstimate4.json";
    Json::Value Root;
    Json::Reader _reader;
    std::ifstream _ifs(jsonFilePath);
    _reader.parse(_ifs, Root);
    for (int i = 0; i < BLOCKBUILDER_CURRENTSIZEESTIMATE4_TIMES; i++) {
        driverBlockBuilder->DriverBlockBuilderCurrentSizeEstimate4(i);
        Json::Value _CurrentSizeEstimate4_Root = Root["CurrentSizeEstimate4" + std::to_string(i)];
        /* return */
        unsigned int _return_actual = _CurrentSizeEstimate4_Root["return"].asUInt();
        /* return */
        unsigned int _return_expected = _CurrentSizeEstimate4_Root["return"].asUInt();
        /* return_expected */
        EXPECT_EQ(_return_expected, _return_actual);
    }
}

最后調(diào)用自動(dòng)構(gòu)建的main函數(shù)運(yùn)行整個(gè)單元測(cè)試過(guò)程。

5.3 參數(shù)捕獲程序構(gòu)建

參數(shù)捕獲是指在程序運(yùn)行過(guò)程中獲取程序的變量信息,主要包括函數(shù)的參數(shù)、全局變量、返回值等。Wings自動(dòng)構(gòu)建獲取參數(shù)的程序,利用插裝技術(shù),將構(gòu)建的捕獲函數(shù)插入源代碼中對(duì)應(yīng)的位置,將獲取的具體信息,寫入值文件,可以將獲取的數(shù)據(jù)作為單元測(cè)試的輸入,在代碼發(fā)生變更后,利用相同的輸入判斷是否得到相同的輸出,進(jìn)行回歸測(cè)試。

Wings的參數(shù)捕獲代碼存儲(chǔ)在paramcaputrecode文件夾中。其中命名規(guī)則同驅(qū)動(dòng)格式一樣,將所有的driver替換為param即可。Wings針對(duì)每個(gè)類生成一個(gè)對(duì)應(yīng)的參數(shù)捕獲類,而參數(shù)捕獲類中針對(duì)每個(gè)函數(shù)生成對(duì)應(yīng)的捕獲參數(shù)、全局變量以及返回值的函數(shù)。c++ 中類的成員變量是私有,無(wú)法從外部獲取,Wings利用插樁技術(shù),對(duì)每個(gè)類插入一個(gè)捕獲函數(shù),來(lái)獲取類的私有成員變量。

參數(shù)捕獲類ParamCaptureBlockBuilder:

class ParamCaptureBlockBuilder
{
public:
  ParamCaptureBlockBuilder();
  ~ParamCaptureBlockBuilder();
  void ParamCapture_Reset1();
  void GlobalCapture_Reset1();
  void ReturnCapture_Reset1();
  void ParamCapture_Add2(const Wings::Slice &key, const std::string &value);
  void GlobalCapture_Add2();
  void ReturnCapture_Add2();
  void ParamCapture_Finish3();
  void GlobalCapture_Finish3();
  void ReturnCapture_Finish3(class Wings::Slice returnType);
  void ParamCapture_CurrentSizeEstimate4();
  void GlobalCapture_CurrentSizeEstimate4();
  void ReturnCapture_CurrentSizeEstimate4(size_t returnType);
};

具體的捕獲函數(shù)不再詳細(xì)說(shuō)明,具體信息可以在Wings官網(wǎng)下載試用版本查看。

第六章 Wings類型以及面向?qū)ο笳Z(yǔ)法特性的支持

Wings能夠支持任意的類型以及面向?qū)ο蟮恼Z(yǔ)法特性。

類型支持:

  • 基本類型,int、double、float、std::string等

  • 任意復(fù)雜的結(jié)構(gòu)類型,結(jié)構(gòu)體、聯(lián)合體、枚舉、鏈表、多級(jí)指針、數(shù)組、樹、圖等

  • 任意復(fù)雜的類對(duì)象,標(biāo)準(zhǔn)庫(kù)容器、自定義模板類

特殊模板:

  • 區(qū)分用戶自定義的類型與系統(tǒng)變量類型(標(biāo)準(zhǔn)庫(kù)頭文件中包含的類型)

  • 類的運(yùn)算符重載函數(shù)

  • void *與函數(shù)指針

  • 識(shí)別類中包含delete與default關(guān)鍵字的函數(shù)進(jìn)行特殊處理

語(yǔ)法支持:

  • 處理static函數(shù)、保護(hù)和私有函數(shù)

  • 類中定義私有結(jié)構(gòu)體

  • 類中包含delete與default關(guān)鍵字的函數(shù)進(jìn)行特殊處理

  • 多態(tài)與復(fù)雜的類繼承

6.1鏈表

針對(duì)鏈表類型,采用比較靈活的賦值方式,考慮到實(shí)際應(yīng)用中的一些因素,針對(duì)鏈表類型,默認(rèn)賦值兩層結(jié)構(gòu),在實(shí)際測(cè)試過(guò)程中,用戶可依據(jù)需要自動(dòng)添加節(jié)點(diǎn)。

6.2 標(biāo)準(zhǔn)庫(kù)容器

Wings能夠支持c++的標(biāo)準(zhǔn)庫(kù)容器,能夠?qū)θ萜鬟M(jìn)行逐層展開,利用不同容器的標(biāo)準(zhǔn)賦值函數(shù)進(jìn)行賦值以取值。

其他類似的容器例如QT中的容器以及boost庫(kù)中的相關(guān)容器,我們?cè)诶^續(xù)支持。

6.3 自定義模板類

一些用戶自定義的模板類類型,Wings能夠識(shí)別是用戶自定義的模板類,Wings依據(jù)實(shí)際程序中的賦值類型,進(jìn)行處理。

6.4 void*與函數(shù)指針

Void*與函數(shù)指針在實(shí)際程序中可以作為函數(shù)參數(shù)或者結(jié)構(gòu)體與類的成員變量,針對(duì)不確定的賦值類型,Wings提供了具體的解決辦法:

① 利用編譯底層技術(shù),對(duì)源程序靜態(tài)分析,獲取真實(shí)類型,對(duì)真實(shí)類型進(jìn)行賦值

② 由用戶在數(shù)據(jù)表格界面配置實(shí)際類型

6.5 特殊模板

在實(shí)際的代碼程序中,會(huì)存在一些類型無(wú)法使用通用的模式全部展開,如一些系統(tǒng)變量(FILE、iostream)、第三方庫(kù)以及一些用戶需要特殊賦值。

Wings是如何針對(duì)以上特殊類型進(jìn)行賦值,舉例如下:

struct sockaddr_in {
        short   sin_family;
        u_short sin_port;
        struct  in_addr sin_addr;
        char    sin_zero[8];
};

步驟如下:

a. 識(shí)別sockaddr_in為系統(tǒng)變量類型,特殊標(biāo)記

b. 檢測(cè)程序中的所有系統(tǒng)變量類型,顯示在模板界面

c. 用戶配置特殊變量,如sin_family

d. 構(gòu)建sockaddr_in對(duì)象

e. 調(diào)用模板,生成驅(qū)動(dòng)

模板配置如下:

圖:6.5模板配置

第七章 數(shù)據(jù)表格

Wings目前測(cè)試用例數(shù)據(jù)采用隨機(jī)生成的方式,支持int、char、double、float、bool、char*類型。數(shù)據(jù)表格可以任意編輯以上類型的數(shù)值。

(1)Wings數(shù)據(jù)表格將會(huì)針對(duì)參數(shù)進(jìn)行展開,假如參數(shù)類型為結(jié)構(gòu)類型,數(shù)據(jù)表格將分層展開結(jié)構(gòu)的類型,到基本類型。

(2) 針對(duì)基本類型的指針類型,例如int *p;Wings處理為不定長(zhǎng)度的一維數(shù)組類型,int **p;處理為不定長(zhǎng)度的二維數(shù)組類型,默認(rèn)長(zhǎng)度為3,數(shù)據(jù)表格界面可以點(diǎn)擊進(jìn)行添加和刪除數(shù)據(jù)。

(3) 針對(duì)不定長(zhǎng)度的數(shù)組作為函數(shù)參數(shù),例如int p[];Wings默認(rèn)長(zhǎng)度為1,用戶依據(jù)需求,在數(shù)據(jù)表格界面進(jìn)行任意添加和修改即可。

圖7-1數(shù)據(jù)表格展示

附錄A
表一:type屬性

ZOA_CHAR_S/ZOA_UCHAR/ZOA_INT/ZOA_UINT/ZOA_LONG/ZOA_ULONG/ZOA_FLOAT/ZOA_UFLOAT/ZOA_SHOTR/ZOA_USHORT/ZOA_DOUBLE/ZOA_UDOUBLE 基本類型
StructureOrClassType 結(jié)構(gòu)體類型
ZOA_FUNC 函數(shù)指針類型
ZOA_UNION 聯(lián)合體類型
ZOA_ENUM 枚舉類型
ClassType 類類型

表二:basetype屬性

BuiltinType 基本類型
ArrayType 數(shù)組類型
PointerType 指針類型
StructureOrClassType 結(jié)構(gòu)體類型
UnionType 聯(lián)合體類型
EnumType 枚舉類型
FunctionPointType 函數(shù)指針類型

表三:其他屬性

Name 代表結(jié)構(gòu)體、類、聯(lián)合體名字
NodeType 代表鏈表類型
parmType 代表函數(shù)參數(shù)類型
parNum 代表函數(shù)參數(shù)個(gè)數(shù)
SystemVar 代表此類型為系統(tǒng)頭文件類型
value 代表枚舉類型的值
bitfield 代表位域類型所占字節(jié)
returnType 代表返回值類型
Field 類成員變量
Method 類構(gòu)造函數(shù)
paramName 類構(gòu)造函數(shù)參數(shù)名
paramType 類構(gòu)造函數(shù)參數(shù)類型
TemplateArgumentType STL結(jié)構(gòu)參數(shù)類型
WingsTemplateArgument STL結(jié)構(gòu)嵌套參數(shù)名字
TemplateArgumentValue STL結(jié)構(gòu)中參數(shù)為具體值
FunctionModifiers 函數(shù)訪問(wèn)權(quán)限
FunctionAttribute 函數(shù)是extern或者static函數(shù)
FuncClassName 函數(shù)所屬類
OperatorFundecl 重載運(yùn)算符函數(shù)
Operator 重載運(yùn)算符類型
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容