轉(zhuǎn):玄歌
終結(jié)DbHelper鬼畫(huà)符 Tdd全攻略
我們現(xiàn)在開(kāi)始為Db增加三個(gè)構(gòu)造方法,這里會(huì)詳細(xì)的演示Tdd的3個(gè)重要的要素:測(cè)試先行、代碼復(fù)審、重構(gòu)。
我們先解釋一下為什么要先寫測(cè)試,再寫代碼,實(shí)際上,有下面的七個(gè)方面的好處:
1、關(guān)注點(diǎn)集中
2、單一的成功被無(wú)數(shù)成功取代,心態(tài)將更為良好:腦力工作,情緒是非常重要的,大家如果正常的統(tǒng)計(jì)自己的有效工作時(shí)間,往往會(huì)發(fā)現(xiàn)自己一天的有效工作狀態(tài)持續(xù)時(shí)間非常短。一個(gè)很經(jīng)典的說(shuō)法,是多數(shù)程序員每天正常工作的時(shí)間平均下來(lái)不超過(guò)一個(gè)小時(shí)。
3、占在類的用戶角度,來(lái)看待類的設(shè)計(jì),有助于
4、建立起整個(gè)項(xiàng)目的質(zhì)量基礎(chǔ)
5、累積下來(lái)的單元測(cè)試,是后期代碼修改的準(zhǔn)則
6、Bug的修復(fù)將更為精準(zhǔn)。
7、單元測(cè)試,就是團(tuán)隊(duì)中其他程序員的使用手冊(cè)。
一魚(yú)六吃,這樣的事情,為什么不做?
代碼復(fù)查,簡(jiǎn)單的說(shuō)就是逐行閱讀代碼,從而找出潛在的問(wèn)題和代碼結(jié)構(gòu)上的壞味道。據(jù)說(shuō),代碼復(fù)查能夠發(fā)現(xiàn)項(xiàng)目中一半以上的Bug,當(dāng)然,在代碼尚未發(fā)布、或者Bug尚未轉(zhuǎn)移到團(tuán)隊(duì)其他成員處的時(shí)候,修復(fù)Bug的成本是最低的。
代碼復(fù)查是很枯燥的工作,自己復(fù)查、團(tuán)隊(duì)成員交互復(fù)查都行。敏捷方法中比較極端的例子是雙人編程,這樣,實(shí)際上代碼復(fù)查貫穿于整個(gè)開(kāi)發(fā)時(shí)間。
重構(gòu),消除代碼中壞的味道,這里最常見(jiàn)的一種現(xiàn)象是“重復(fù)的代碼”。
由于有大量的單元測(cè)試,重構(gòu)過(guò)程中保持全部單元測(cè)試通過(guò),有助于實(shí)現(xiàn)我們的目標(biāo):僅僅調(diào)整代碼的結(jié)構(gòu),而不會(huì)令功能實(shí)現(xiàn)上出現(xiàn)問(wèn)題。
代碼質(zhì)量,并不是代碼工作如何精準(zhǔn),而是代碼的結(jié)構(gòu)是否做到了最簡(jiǎn)化。請(qǐng)注意,稍稍不留意,代碼增加的少許復(fù)雜性,會(huì)令同事閱讀困難、甚至令自己閱讀困難,各處的復(fù)雜性累積之后,會(huì)造成Bug數(shù)量的大幅增長(zhǎng),維護(hù)也變得困難。這里可以參考用戶體驗(yàn)的一句名言“多一次擊鍵,用戶可能永遠(yuǎn)不會(huì)使用這項(xiàng)功能”。
嘮叨完畢,我們先做好準(zhǔn)備。
我們創(chuàng)建一個(gè)單元測(cè)試項(xiàng)目,引用我們的工作項(xiàng)目Faster.Data類庫(kù)項(xiàng)目。在單元測(cè)試項(xiàng)目中增加一個(gè)配置文件App.config,我們將數(shù)據(jù)庫(kù)連接信息保存在這個(gè)配置文件中。
單元測(cè)試中最常見(jiàn)的問(wèn)題,是“數(shù)據(jù)庫(kù)如何測(cè)試?”
我曾經(jīng)饒有興趣的使用Mock,但最終對(duì)自己定下了規(guī)矩,即今后絕不使用任何Mock。原因:Mock對(duì)象會(huì)大幅增加單元測(cè)試的簡(jiǎn)潔性,構(gòu)建Mock對(duì)象有時(shí)候也需要大量的工作,充滿了Mock的單元測(cè)試可讀性很差,團(tuán)隊(duì)成員需要全體掌握相關(guān)的知識(shí)從而導(dǎo)致對(duì)開(kāi)發(fā)人員的門檻提高,這也意味著成本的提高。
不過(guò),在這個(gè)類庫(kù)創(chuàng)建完畢后,數(shù)據(jù)庫(kù)讀寫操作就有了很好的基礎(chǔ),今后所有應(yīng)用項(xiàng)目則根本無(wú)需對(duì)數(shù)據(jù)庫(kù)讀寫進(jìn)行單元測(cè)試。
我的方法是,建立測(cè)試數(shù)據(jù)庫(kù),保證數(shù)據(jù)庫(kù)所有表格處于空白的無(wú)記錄狀態(tài),即針對(duì)每一項(xiàng)測(cè)試,測(cè)試前要準(zhǔn)備數(shù)據(jù),測(cè)試后毀尸滅跡。
那么,現(xiàn)在開(kāi)始為Db增加構(gòu)造方法,我們先做最原始的:
第一步 在類圖中為Db添加一個(gè)構(gòu)造方法 Db(string connectionString,string providerName)
為什么先做這個(gè)?因?yàn)檫@是與配置文件無(wú)關(guān)的,Db(string configName),這個(gè)從配置文件中獲取上述兩個(gè)參數(shù),Db()則默認(rèn)的使用ApplicationServices配置項(xiàng)獲取上述兩個(gè)參數(shù)。我們顯然要先做最簡(jiǎn)單的。換句話說(shuō):先解決構(gòu)造問(wèn)題,再解決配置文件的問(wèn)題。
第二步,切換到這個(gè)構(gòu)造方法的代碼,右鍵,選擇創(chuàng)建單元測(cè)試。此時(shí),Ide幫我們?cè)谙率琼?xiàng)目中增加了一個(gè)代碼文件,測(cè)試類的名稱為DbTest,嗯,類名加上Test。
那么,這是第一個(gè)基本的概念,測(cè)試是以類為單位的,每一個(gè)待測(cè)試的類,對(duì)應(yīng)一個(gè)測(cè)試類。多數(shù)情況下,類的每一個(gè)public成員,對(duì)應(yīng)一個(gè)測(cè)試方法。我們要牢記兩點(diǎn):1、不針對(duì)私有成員寫測(cè)試;2、每一個(gè)測(cè)試方法都要足夠的簡(jiǎn)單,突出該方法的目的。
第三步,我們?cè)跍y(cè)試方法中,寫上這樣的代碼
Db db= new ("連接字符串","提供者名稱");
Assert.IsNotNull(db); //我們期望上一行創(chuàng)建的db對(duì)象不為空
第四步,運(yùn)行測(cè)試,出現(xiàn)綠色的“成功”提示。好,現(xiàn)在這個(gè)測(cè)試已經(jīng)通過(guò)。
第五步,現(xiàn)在我們要確定Db已經(jīng)正常的連接到數(shù)據(jù)庫(kù)。對(duì),如您所說(shuō),Db的State狀態(tài)必須為Open
我們?cè)跍y(cè)試方法下,再增加一行
Assert.AreEqual(ConnectionState.Open,Db.State);//期望創(chuàng)建的連接已經(jīng)打開(kāi)
運(yùn)行測(cè)試,當(dāng)然失敗了,因?yàn)槲覀冞沒(méi)有做真正的連接工作。
第六步,我們?yōu)閯倓倢懙腄b的構(gòu)造方法中增加如下的代碼:
factory = DbProviderFactories.GetFactory(provider);
connection = factory.CreateConnection();
connection.ConnectionString =connectionString;
connection.Open();
第七步,運(yùn)行測(cè)試,通過(guò)。嗯,現(xiàn)在這項(xiàng)工作完成。
按照同樣的方式,創(chuàng)建其他兩個(gè)構(gòu)造方法。比如:
第八步:從某個(gè)配置項(xiàng)中定義的連接字符串,創(chuàng)建連接
var setting = ConfigurationManager.ConnectionStrings[config];
factory = DbProviderFactories.GetFactory(provider);
connection = factory.CreateConnection();
connection.ConnectionString =connectionString;
connection.Open();
第九步 重構(gòu)
我們簡(jiǎn)單的審閱代碼,會(huì)發(fā)現(xiàn)第六步和第八步的代碼,顯然有多數(shù)重復(fù)。那么,創(chuàng)建一個(gè)私有方法CreateConnection,包含如下代碼:
factory = DbProviderFactories.GetFactory(provider);
connection = factory.CreateConnection();
connection.ConnectionString =connectionString;
然后,第六步的代碼便是:
CreateConnection(connectionString, provider);
第八步的代碼變?yōu)椋?br />
var setting = ConfigurationManager.ConnectionStrings[config];
CreateConnection(setting.ConnectionString, setting.ProviderName);
重復(fù),是最常見(jiàn)的“壞味道”,這里是最淺顯的例子。
如此,我們通過(guò)創(chuàng)建三個(gè)單元測(cè)試、從而創(chuàng)建三個(gè)構(gòu)造方法,完成了這一組任務(wù)。過(guò)程中,根據(jù)進(jìn)度,在Tfs團(tuán)隊(duì)項(xiàng)目中將各項(xiàng)task的狀態(tài)分別由in progress 改為done。
看看,比較滿意吧?運(yùn)行所有測(cè)試,通過(guò)。相關(guān)的任務(wù),done。將代碼簽入到源代碼服務(wù)器,接著做剩下的其他工作… |