mockdb使用心得

這兩天在寫累積量模塊的單元測試用例,由于AcmCalc.cpp里面涉及大量數(shù)據(jù)庫操作洲脂,三千多行代碼恐锦,為了增強(qiáng)對該文件的防護(hù),提升覆蓋率陕贮,就想著通過mockdb實(shí)現(xiàn)一下潘飘,但事與愿違,一直在碰壁局骤,好好的數(shù)據(jù)放到mockdb怎么就撈不出來呢暴凑,于是花了兩天時間研究了一下mockdbmockdb的實(shí)現(xiàn)。

其實(shí)很簡單凯傲,從代碼里看來嗦篱,無外乎幾個STL標(biāo)準(zhǔn)模板

typedef vector<TMockDBField> MockFields;

typedef list<MockFields>?MockRecords;

typedef map<string,MockRecords>?MockTables;

typedef map<string,MockTables *>?MockDatas;

從上面幾個容器灸促,就構(gòu)建出了一個簡易數(shù)據(jù)庫涵卵。第一個容器荒叼,就是用來存放表里的每個字段,TMockDBField類里存放的數(shù)據(jù)有如下幾個:

?int data_type;?

?long long lvalue;?

?string svalue;?

?char name[128];?

?bool bNull;

將一個表里的所有字段存放在第一個vector里面之后坏晦,就可以生成一個表昆婿,記錄了同一張表里的多條記錄蜓斧。通過第二個list存放,這時就需要有一個表了多律,于是第三個map就出現(xiàn)了第三個map里的前面string就是表名搂蜓,后面的list就是這個表里的所有記錄辽装。對于第四個map,這里應(yīng)該是為了創(chuàng)建多個數(shù)據(jù)庫準(zhǔn)備的殉挽。大概了解mockdb數(shù)據(jù)存放方式之后斯碌,就可以進(jìn)行插入更新刪除操作了肛度。

可惜的是,這些DML操作也實(shí)現(xiàn)得太簡單了冠骄,具體情況如下:

本來我是通過:

static const char * const UPDATE_SUBS_ACM_DAILY_SQL="UPDATE SUBS_ACM_DAILY SET VALUE = VALUE + :VALUE \n"

" WHERE \n"

" SUBS_ID = :SUBS_ID AND RESOURCE_ID = :RESOURCE_ID AND DATE_STAMP = :DATE_STAMP";

語句去實(shí)現(xiàn)更新的加袋,如果有記錄职烧,則更新防泵。如果沒有蝗敢,則實(shí)現(xiàn)如下語句:

static const char * const INSERT_SUBS_ACM_DAILY_SQL="INSERT INTO SUBS_ACM_DAILY (SUBS_ID,RESOURCE_ID,VALUE,DATE_STAMP) \n"

" VALUES \n"

" (:SUBS_ID,:RESOURCE_ID,:VALUE,:DATE_STAMP)";

這沒問題前普。當(dāng)我第一個用例去實(shí)現(xiàn)訂戶日累積的時候,能正常插入數(shù)據(jù)骡湖。

但是當(dāng)我第二個用例去對商品每天累積時峻厚,由于是同一張表SUBS_ACM_DAILY我先去實(shí)現(xiàn)update操作,mockdb居然就把我第一個用例的數(shù)據(jù)給更新掉了浦夷,更新掉了辜王。。肥缔。汹来。SUBS_ACM_DAILY表字段如下:

*SUBS_ID

*RESOURCE_ID

*DATE_STAMP?

VALUE

我在想收班,它是通過什么去更新的。發(fā)現(xiàn)它自己能創(chuàng)建出索引摔桦,所有update語句where后面所帶的字段,它都自動認(rèn)為是索引瘦穆。那也是很好的赊豌,既然能自動為SUBS_ID、RESOURCE_ID熙兔、DATE_STAMP加上索引,那就進(jìn)行匹配吧麸锉。結(jié)果匹配得也是神奇舆声,三個字段,只要有一個字段匹配上碱屁,就算是找到記錄了蛾找,前兩個字段都沒有匹配打毛,只有第三個DATE_STAMP相同狈醉,于是乎就update了耍共。如下函數(shù)就是去找數(shù)據(jù)庫匹配的記錄:

bool TMockDBQuery::FindSatisfiedRecord()

{

? ? bool bSatisfied = false;

? ? for (MockFields::iterator itF = m_fields.begin(); itF !=m_fields.end(); itF++)//檢查綁定的參數(shù)

? ? {

? ? ? ? TMockDBField tInputField = *itF;//待檢查的參數(shù)


? ? ? ? if(!IsIndex(tInputField.AsName()))//這里看上去是在匹配索引

? ? ? ? {

? ? ? ? ? ? continue;

? ? ? ? }


? ? ? ? MockFields vFields = *itCur;

? ? ? ? for (MockFields::iterator it = vFields.begin(); it != vFields.end(); it++)//對比fields

? ? ? ? {

? ? ? ? ? ? TMockDBField tSaveField = *it;//record中的參數(shù)

? ? ? ? ? ? if (tInputField == tSaveField)

? ? ? ? ? ? {

? ? ? ? ? ? ? ? bSatisfied = true;//這里只有一個索引值匹配時奥邮,就認(rèn)為找到返回了,淚奔脚粟。核无。。

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? }

? ? ? ? ? ? else

? ? ? ? ? ? {

? ? ? ? ? ? ? ? bSatisfied = false;

? ? ? ? ? ? }? ? ? ? ? ? ? ?

? ? ? ? }

? ? }

? ? return bSatisfied;

}

接下來的更新也是很神奇的噪沙。update語句里的SET VALUE = VALUE + :VALUE根本就不會去執(zhí)行吐根,只是將新記錄的value去對原記錄的覆蓋。

??? case OPER_UPDATE:

??? if( it != pTables->end())

??? {

??????? m_pRecords=&(it->second);

??????? itCur=m_pRecords->begin();

??????? while(itCur!=m_pRecords->end())

??????? {

??????????? //查找滿足條件的記錄

??????????? bool bSatisfied =FindSatisfiedRecord();

??????????? if (bSatisfied)

??????????? {

??????????????? //更新

??????????????? for (MockFields::iterator itF =m_fields.begin(); itF !=m_fields.end(); itF++)//檢查綁定的參數(shù)

??????????????? {

????????????????? ??TMockDBField tBindField = *itF;//綁定的參數(shù)???????????????????


??????????????????? MockFields vFields =*itCur;

??????????????????? for (MockFields::iteratoriter = itCur->begin(); iter != itCur->end(); iter++)//對比fields

??????????????? ????{

??????????????????????? TMockDBFieldtRecordField = *iter;//record中的參數(shù)

??????????????????????? if(!iter->IsName(tBindField.AsName()))//名字不同喜爷,跳過更新

??????????????????????????? continue;


???????????????????????if(!IsIndex(iter->AsName())) //如果不是索引字段萄唇,更新

??????????????????????? {

??????????????????????????? cout <<"before: " <ToString()<

??????????????????????????? *iter = tBindField;

??????????????????????????? cout <<"after : " <ToString()<

??????????????????????? }

??????? ????????????}

??????????????? }

??????????????? iRowsAffected++;

??????????????? cout<<"TDBQuery::Execute() for update->OK!" << endl;

??????????? }

??????????? itCur++;

??????? }

??? }

gdb打印的日志顯示:

(gdb) n

before:TDBField[VALUE]:? Type?? = 0;?Values = ;? lvalue = 60;

243???????????????????????????????????? *iter =tBindField;

(gdb) n

244???????????????????????????????????? cout<<? "after : "<ToString()<

(gdb) n

after :TDBField[VALUE]:? Type?? = 0;?Values = ;? lvalue = 240;

236???????????????????????????????? TMockDBFieldtRecordField = *iter;//record中的參數(shù)

(gdb) n

這里之前的累積量就是60另萤,本次累積量就是240四敞,直接覆蓋。俩由。癌蚁。


導(dǎo)致第二個測試用例去撈數(shù)據(jù)的時候,就沒撈到

再次詳細(xì)分析了一下為什么沒撈到碘梢,是怎么匹配的關(guān)鍵字

select語句的索引創(chuàng)建時伐蒂,是在SET_PARAM時將需要匹配的條件字段放在了vector m_fields中逸邦,這個存放規(guī)則也有問題,因?yàn)槿绻阌卸鄠€匹配字段雷客,不小心將最后一個需要比較的字段放到里面桥狡,而此時表里正好有一條記錄的這個字段能夠匹配,程序卻也認(rèn)為這條記錄就是你需要撈取的記錄部逮。這段代碼如下:

??? for (; itCur != m_pRecords->end();itCur++)//遍歷表里每一條record

??? {

??????? for (MockFields::iterator itF =m_fields.begin(); itF !=m_fields.end(); itF++)//檢查綁定的參數(shù)嫂易,就是你select時where里的條件

??????? {

??????????? TMockDBField tInputField = *itF;//待檢查的參數(shù)

??????????? if(!IsIndex(tInputField.AsName()))

??????????? {

??????????????? continue;

??????????? }

??????????? MockFields vFields = *itCur;

??????????? for (MockFields::iterator it =vFields.begin(); it != vFields.end(); it++)//對比fields

??????????? {

??????????????? TMockDBField tSaveField =*it;//record中的參數(shù)

??????????????? if (tInputField == tSaveField)

??????????????? {???????????????????

??????????????????? bFind = true;//當(dāng)最后一個需要匹配的條件字段恰好跟表里當(dāng)時那條記錄對應(yīng)字段相等時怜械,這里就認(rèn)為找到記錄了

??????????????????? break;

??????????????? }

??????????????? else

??????????????? {???????????????????

??????????????????? bFind = false;

??????????????? }???????????????

??????????? }

??????? }

??????? if (m_fields.size() == 0)

??????? {

??????????? bFind = true;

??????? }

??????? if (bFind)

??????? {

??????????? m_outputFields = *itCur;

??????????? itCur++;

??????????? return true;

??? ????}

??? }

這個地方我通過在設(shè)置:

??? 原始:

???m_pOdbcQuery->SetParameter("SUBS_ID",pRatableEvent->GetSubsId());

???m_pOdbcQuery->SetParameter("DATE_STAMP",iDate);

???m_pOdbcQuery->SetParameter("RESOURCE_ID", iResourceId);

??? 改為:

???m_pOdbcQuery->SetParameter("SUBS_ID",pRatableEvent->GetSubsId());

???m_pOdbcQuery->SetParameter("RESOURCE_ID", iResourceId);

???m_pOdbcQuery->SetParameter("DATE_STAMP",iDate);//因?yàn)檫@個字段跟第一個用例里的相同,所以最后匹配這個字段時相同享完,就認(rèn)為是撈取出來了

最終結(jié)果跑出來確是正確的:

[?????? OK ] TAcmCalcTest.Give_duration1_AcmType7_When_rum60_Then_Acm60(7 ms)

[ RUN ]TAcmCalcTest.Give_duration181_AcmType9_When_rum60_Then_Acm240

[?????? OK ]TAcmCalcTest.Give_duration181_AcmType9_When_rum60_Then_Acm240 (9 ms)

[----------] 2

tests from TAcmCalcTest (16 ms total)

[----------]

Global test environment tear-down

[==========] 2

tests from 1 test case ran. (497 ms total)

[? PASSED?] 2 tests.

這里的成功般又,也多虧了前面update時沒有加上原始值的bug,不然這里撈出來就是240+60=300了


還沒想到能有什么好的方式去規(guī)避這個問題

想過每個用例跑完之后rollback巍佑,但是之前訂戶資料,資費(fèi)等信息也在里面萤衰,擔(dān)心rollback所有數(shù)據(jù)都沒有堕义,而且也沒有提供這樣的接口,因?yàn)橐婚_始就沒有commit動作

感覺mockdb只能是最簡單的存放單條數(shù)據(jù)脆栋,而且不能寫復(fù)雜的sql,包括where后面帶and或者or,也不能在update里帶計算倦卖。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市椿争,隨后出現(xiàn)的幾起案子怕膛,更是在濱河造成了極大的恐慌,老刑警劉巖秦踪,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件褐捻,死亡現(xiàn)場離奇詭異椅邓,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)景馁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人慕购,你說我怎么就攤上這事聊疲。” “怎么了沪悲?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵获洲,是天一觀的道長。 經(jīng)常有香客問我殿如,道長贡珊,這世上最難降的妖魔是什么最爬? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮门岔,結(jié)果婚禮上爱致,老公的妹妹穿的比我還像新娘。我一直安慰自己寒随,他們只是感情好糠悯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著妻往,像睡著了一般互艾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上讯泣,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天纫普,我揣著相機(jī)與錄音,去河邊找鬼好渠。 笑死昨稼,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的晦墙。 我是一名探鬼主播悦昵,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼晌畅!你這毒婦竟也來了但指?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤抗楔,失蹤者是張志新(化名)和其女友劉穎棋凳,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體连躏,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剩岳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了入热。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拍棕。...
    茶點(diǎn)故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖勺良,靈堂內(nèi)的尸體忽然破棺而出绰播,到底是詐尸還是另有隱情,我是刑警寧澤尚困,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布蠢箩,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏谬泌。R本人自食惡果不足惜滔韵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望掌实。 院中可真熱鬧陪蜻,春花似錦、人聲如沸潮峦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽忱嘹。三九已至嘱腥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拘悦,已是汗流浹背齿兔。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留础米,地道東北人分苇。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像屁桑,于是被迫代替她去往敵國和親医寿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理蘑斧,服務(wù)發(fā)現(xiàn)靖秩,斷路器,智...
    卡卡羅2017閱讀 134,662評論 18 139
  • 原文:https://my.oschina.net/liuyuantao/blog/751438 查詢集API 參...
    陽光小鎮(zhèn)少爺閱讀 3,826評論 0 8
  • 題目 題意 有一個軍隊竖瘾,每個人都有一個個性值沟突,主角時不時會增加、或者移除一個指定個性值的戰(zhàn)士捕传,也可能試圖添加一個指...
    MoorLi閱讀 349評論 0 0
  • 原作者:張江作者簡介:北京師范大學(xué)系統(tǒng)科學(xué)學(xué)院副教授張江原發(fā)平臺:混沌巡洋艦微信二維碼微信號:chaoscruis...
    星辰進(jìn)取號閱讀 18,878評論 3 16