第一章 Wings企業(yè)級(jí)單元測(cè)試自動(dòng)編碼引擎誕生的背景
隨著科技的飛速發(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ù)捕獲等代碼和值。

上述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)
模板配置如下:

第七章 數(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)行任意添加和修改即可。

附錄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)算符類型 |