iOS開發(fā)之單元測試

本文側(cè)重講述如何在iOS程序的開發(fā)過程中使用單元測試帆谍。使用Xcode自帶的OCUnit作為測試框架废睦。

一施符、單元測試概述

單元測試作為敏捷開發(fā)實踐的組成之一,其目的是提高軟件開發(fā)的效率慈迈,維持代碼的健康性若贮。其目標是證明軟件能夠正常運行,而不是發(fā)現(xiàn)bug(發(fā)現(xiàn)bug這一目的與開發(fā)成本是正相關(guān)的痒留,雖然發(fā)現(xiàn)bug是保證軟件質(zhì)量的一種手段谴麦,但是很顯然這與降低軟件開發(fā)成本這一目的背道而馳)。它是對軟件質(zhì)量的一種保證伸头,例如重構(gòu)之后我們需要保證軟件產(chǎn)品的正常運行匾效。

很多人認為編寫單元測試沒有用是認為單元測試并不能保證一定能減少bug發(fā)生的幾率,而由于編寫單元測試一定會花費一定的時間與精力恤磷,因而必然的會增加成本面哼。客觀的說扫步,造成這種原因很大的程度上是程序員的水平不夠高魔策。我認為使用使用單元測試帶來巨大好處的必要條件如下所示:

程序員本身的編程水平--是否有較多的代碼經(jīng)驗,是否熟練掌握重構(gòu)
程序員對項目的認知--是否能正確理解軟件或模塊的需求
項目質(zhì)量--是否穩(wěn)定河胎,是否長期多版本闯袒,是否需要應(yīng)對較多變化

如果程序員水平較高,對需求理解較為清晰游岳,項目需要面對較多的變化政敢,那么毫無疑問單元測試對于軟件非常有益。假如軟件功能簡單且開發(fā)周期短胚迫,不需要進行復(fù)雜的維護工作喷户,那么單元測試的意義并不大。

優(yōu)秀的單元測試實踐的好處:

好的單元測試就是一份好的文檔访锻,并且比文檔更能為程序員所接受褪尝,它直接描述了測試員對受測代碼的結(jié)果所持的預(yù)期。
當代碼由別人維護時(或自己進行重構(gòu)時)朗若,通過單元測試的約束恼五,才能保證在加入新功能或修改舊功能時代碼的正確性昌罩。
由于單元測試的自動化執(zhí)行哭懈,保證了在整個開發(fā)流程中代碼都會被測試,這非常符合XP思想茎用。
保證在面對軟件功能的變化時遣总,程序員可以較為放心的進行代碼重構(gòu)睬罗,而不必擔(dān)心是否破壞了原有功能。
好的單元測試可以降低bug數(shù)量旭斥,而對于項目管理來說容达,修改bug這個過程是無法制定計劃的,可以使軟件的開發(fā)流程更容易掌控垂券。
可以由老程序員編寫描述某個類行為的測試花盐,以此指導(dǎo)新程序員對類的編碼。
……

好處還有很多菇爪,但最重要的一點就是保證了軟件質(zhì)量的同時算芯,由于減少bug和應(yīng)對變化造成的回歸bug的產(chǎn)生等,提高了勞動生產(chǎn)率凳宙。而且熙揍,在敏捷流程中,使用單元測試是必須掌握的手段氏涩,否則就沒發(fā)保證重構(gòu)的正確性届囚,從而造成代碼無法面對變化。

二是尖、iOS的單元測試概述

剛接觸客戶端編程時意系,我在很長一段時間內(nèi)都想不通對于客戶端程序如何編寫單元測試。單元測試本質(zhì)上說白了就是用一些斷言來判定結(jié)果饺汹,而這種方式是如何應(yīng)用到具有復(fù)雜交互的界面測試上來的呢昔字?

我們要做的就是將客戶端代碼轉(zhuǎn)化為易于測試的代碼。什么樣的代碼易于測試呢首繁?它至少是這樣的:

  • 1作郭、被測方法需要產(chǎn)生可測量的結(jié)果。
  • 2弦疮、類之間的關(guān)系應(yīng)該是松耦合的夹攒。

其中第一條是必要條件。使用斷言這種形式指明了測試的方法最終要造成某些可以度量的結(jié)果胁塞。因而咏尝,我們需要盡量的將展示和業(yè)務(wù)邏輯分離開來。展示的代碼是沒法測試的啸罢,例如有的方法只是播放動畫编检。而業(yè)務(wù)邏輯最終都會造成一些數(shù)據(jù)的改變,這是容易測試的扰才。

大略的講允懂,作為一個iOS程序員來說,首先要了解一個叫做MVC的模式衩匣。這個模式定義了Cocoa Touch框架的總體結(jié)構(gòu)蕾总。在iOS程序中粥航,我們也需要按照這種模式進行界面代碼的編寫。這樣設(shè)計出來的類具有較好的結(jié)構(gòu)生百,且比較適合于做單元測試递雀。

然后一定要懂得不停重構(gòu)代碼,這樣我們才能使代碼不停地改善蚀浆,不停地變得更加適合單元測試缀程。

有一些框架可以幫助大家更好的測試,分別是OCUnit市俊、GTM杠输、GHUnitCATCH秕衙、OCMock蠢甲,但目前對我來說,OCUnit足夠用了据忘。作為蘋果官方提供的測試框架鹦牛,它最大的優(yōu)點就是簡單易用。

三勇吊、單元測試實踐

下面是一些我所理解的單元測試中比較好的實踐曼追。

顧名思義,單元測試面向的對象是單元汉规,這個專有名詞源自編譯器領(lǐng)域的術(shù)語“編譯單元”礼殊。在面向過程中,指的是函數(shù)针史,而在面向?qū)ο笾芯祝傅耐ǔ>褪恰邦悺薄R蚨恼恚總€功能類都應(yīng)該提供對應(yīng)的單元測試婚陪。

實踐1 每個功能類都應(yīng)提供單元測試,且每一個測試類频祝,只依賴于其要測試的受測類泌参。使用偽造對象可以避免對其他類的依賴外冀。

解釋 保證一個測試類只關(guān)注一個被測類宣赔,當測試不通過時,就能迅速的定位到是誰發(fā)生了錯誤答姥,而不會受到其他類的干擾漓糙。

簡單的數(shù)據(jù)類等可以不提供铣缠,但是要保證該測試的都要覆蓋到。并不存在一種合適的度量指標可以量化地判斷某種單元測試方案是否成功。常用的標準(代碼覆蓋率和成功執(zhí)行的測試用例數(shù))都可以在受測軟件的質(zhì)量不變的情況下人為的修改(作假)攘残。當然拙友,在無法確保程序員素質(zhì)的情況下为狸,作為沒有辦法的辦法歼郭,使用這種標準也是可以的(或者無奈的說,必須的)辐棒。單元測試需要程序員自己把關(guān)病曾,關(guān)注哪些功能確實需要測試覆蓋。這也就是前面所說的一些程序員不相信單元測試可以提高生產(chǎn)率的理由--它更多的依賴于程序員的素質(zhì)漾根,這是沒有保證的泰涂。但同樣的,由于敏捷是一種以人為本的思想實踐辐怕,因而這種行為似乎又是一種必然逼蒙。

實踐1.1 使用偽造類避免對其他類的依賴

解釋 避免依賴的一種手段。
例如寄疏,某個被測的方法聲明是這樣的:

-(void)xxxx:(Person *)person;

如果測試時傳入Person的話是牢,就造成了測試類依賴于兩個類。當由于person中的錯誤引發(fā)測試不通過時陕截,就不能迅速的定位到受測類中是否有問題驳棱。遇到這種情況,就可以使用偽造類农曲。假如方法中只使用了person的一個屬性name社搅,那么可以將方法名重構(gòu)為

-(void)xxxx:(id)person;(此處id有待商榷,只是這樣做最簡單)

然后在單元測試的target中添加只包含name屬性的fakePerson來作為偽造類乳规。這樣形葬,一旦發(fā)生錯誤就可以迅速的推測出錯誤的來源。

實踐1.2 使用偽造環(huán)境避免其他環(huán)境的干擾暮的。

解釋 適合于異步的方法測試荷并。

很經(jīng)常遇到的一種情況是測試有網(wǎng)絡(luò)環(huán)境的代碼。由于異步的存在青扔,這會造成測試代碼不好寫源织。一種簡單的解決方法是,我們假定網(wǎng)絡(luò)一定是通暢的微猖,則我們測試的代碼將分為兩部分谈息,即拼裝發(fā)送功能和接收解析功能。假如發(fā)送和接收功能各自都能通過測試凛剥,那么我們大約可以確定這個異步方法的正確性侠仇。另一種方法是使用GHUnit,它支持異步代碼的測試。

實踐2 測試用例(方法)名應(yīng)該是自解釋的且是獨立的逻炊。

解釋 基本功互亮。

如果被測試類的名稱是XXX,那么測試類可以命名為XXXTests余素。而對于其中要測試的功能豹休,命名應(yīng)該是自解釋的。這可以在發(fā)現(xiàn)錯誤時盡快的定位問題所在桨吊。例如威根,如果某個屬性obj應(yīng)該是非空的,那么我們可以將其命名為:

-(void)testObjNotNil{}

每個方法目標應(yīng)該是單一的视乐,大多數(shù)情況下每個方法內(nèi)都只有一個斷言語句洛搀;方法不應(yīng)該依賴于其他方法的結(jié)果作為輸入,保證原子性佑淀。

實踐3 斷言語句需要解釋測試者的意圖留美。

解釋 基本功

每種單元測試框架都提供了很多斷言語句,從根本上來說它們都是一樣的伸刃。但是測試者需要根據(jù)自己的目的選擇適當?shù)恼Z句谎砾,這樣才可以讓別人閱讀測試代碼時理解用例設(shè)計的目的。例如對于STAssertNil和STAssertNotNil等等奕枝。

實踐4 判斷某個意圖有沒有達到的很好的方法是檢測方法影響的數(shù)據(jù)有沒有合理的變化棺榔。

解釋 基本功

由于單元測試是使用斷言語句來做判斷的,因而最容易做的就是判斷數(shù)據(jù)的變化隘道。這也就限定了單元測試能測試的方法范圍症歇,即引起數(shù)據(jù)變化的方法。對于一些純展示的方法谭梗,例如播放一段特效忘晤,這種方法是無法靠單元測試來進行約束的。測試數(shù)據(jù)的特性包括取值范圍(int激捏、float等)设塔,排列順序(NSArray等),類型等等远舅。

實踐5 運用重構(gòu)的手段使方法變得易于被測試闰蛔。

解釋 單元測試是保障重構(gòu)安全的手段,重構(gòu)也可以使代碼易于被測試图柏。

什么樣的代碼是容易進行單元測試的序六?最簡單的一點就是,每個被測方法都應(yīng)該是功能單一的蚤吹。當然例诀,這也是代碼規(guī)范中應(yīng)該做到的随抠。方法的功能單一,則測試方法的斷言也會比較好確定繁涂。如果你發(fā)現(xiàn)某個方法很難進行測試拱她,則就應(yīng)該對這個方法進行拆分重構(gòu)。

實踐5.1 面向抽象設(shè)計類之間的關(guān)系扔罪。

解釋 利于偽造類的實現(xiàn)秉沼。

類之間通訊如果依賴于抽象(接口),則可以較容易的使用偽造類步势。參照實踐1.1氧猬。

實踐6 運用自上而下的方式構(gòu)建類背犯。

解釋 自上而下的方式可以使類的功能明確坏瘩,類的構(gòu)成將會清晰緊湊,不會出現(xiàn)一些廢方法漠魏。

先確定類需要負擔(dān)的責(zé)任倔矾,以此來確定類具有的公有方法以及屬性。通過重構(gòu)將公有方法中的代碼轉(zhuǎn)化為私有方法柱锹,以使方法盡量短小緊湊哪自。

實踐6.1 應(yīng)對所有暴露的屬性和方法提供測試,私有方法則不必禁熏。

解釋 如果運用自上而下的方式構(gòu)建類壤巷,則理論上私有方法應(yīng)該都是公有方法重構(gòu)而得到的。實際上測試公有方法時這些私有方法都應(yīng)該被測試到了瞧毙。而且胧华,由于私有方法相對公有方法來說發(fā)生變動的可能性很大,會造成不必要的修改測試代碼的成本宙彪。

回調(diào)方法不屬于私有方法矩动,也需要進行測試。

實踐6.2 回調(diào)方法的測試方法是直接調(diào)用释漆。

解釋 基本功

由于回調(diào)方法一般是異步和不可觸發(fā)的(按正常流程)悲没,例如網(wǎng)絡(luò)事件的返回和按下按鈕的觸發(fā)事件。因而男图,測試的時候要直接調(diào)用來對其流程進行檢測示姿。例如某個按鈕的touch up inside事件:

-(void)buttonPressed:(id)sender;

可以根據(jù)方法中用到的方法、屬性偽造一個FakeButton按鈕作為參數(shù)傳遞進行測試逊笆。

實踐6.2 測試私有的方式栈戳,KVC、子類化和類別览露。

解釋 基本功荧琼。

遇到需要通過驗證私有數(shù)據(jù)才能編寫的測試時,可以考慮使用KVC和子類化。子類繼承于被測類命锄,只包含于單元測試target堰乔,其作用就是在不該變受測類的情況下,使受測類具有某些易于被測的能力脐恩。

實踐7:變化需要新測試的支持镐侯。

解釋:保證測試的覆蓋度。

就像敏捷中提到的“改變需要抽象”一樣驶冒,在測試中改變需要新的測試苟翻。當然,度依然由程序員自己掌控骗污。

四崇猫、一般流程

使用OCUnit最大的好處就是流程非常的簡單,簡單到讓你覺得非常愉悅需忿。由于有XCode的支持诅炉,添加測試變得異常簡單。只要在新建工程時勾選“Include Unit Tests”屋厘,就會自動的加入一個示例涕烧。然后再需要添加新的單元測試時,新建一個“Objective-C test case class”就可以了汗洒。

測試文件中议纯,只要知道setUp是初始化的地方,tearDown是結(jié)束清理的地方溢谤,而且它們在每個用例方法執(zhí)行時都會重新執(zhí)行--這保證了測試用例的原子性瞻凤。然后知道每個測試用例都是以test作為前綴的,并且無返回值溯香。然后在方法中編寫斷言語句就可以了鲫构。輸入STAssertxxxxx就可以看到它們的聯(lián)想提示。編寫完成后玫坛,執(zhí)行菜單Product->Test结笨,單元測試就完成了!

五湿镀、測試驅(qū)動(TDD)

敏捷當中提到了TDD這種開發(fā)方式炕吸。TDD的主旨是使開發(fā)者對其編寫的代碼更有信心,使開發(fā)者修改代碼時心里更加踏實勉痴。對于其總結(jié)赫模,還是引用原文比較妥當:“測試驅(qū)動開發(fā)的妙處即在于,它以需求為引領(lǐng)蒸矛,通過測試的形式瀑罗,來指導(dǎo)開發(fā)者進行軟件的設(shè)計與架構(gòu)胸嘴,并編寫出最為精煉的代碼,使得測試用例運行通過斩祭。經(jīng)過適當?shù)闹貥?gòu)之后劣像,測試用例與產(chǎn)品代碼可達到較為健康的狀態(tài)〈菝担”也就是上面提到的耳奕,通過自上而下的形式設(shè)計類,通過單元測試來不停地審視和重構(gòu)類诬像,從而達到代碼的健康屋群。

如果在代碼寫完之后在編寫單元測試,那么就體現(xiàn)不出這種模式的好處了坏挠。這就好像寫完代碼再補文檔一樣芍躏,沒有什么意義。測試應(yīng)該在代碼開始之前癞揉,或者在代碼編寫中不停地進行編寫更新纸肉,這樣才能使代碼不停進步溺欧。這也正是TDD的意思喊熟。

六、總結(jié)

單元測試的代碼如此簡單姐刁,但是想寫好單元測試卻并不是一件簡單的事情芥牌。它需要程序員比較深的功底。由于個人水平所限聂使,有一些東西說的比較啰嗦壁拉。把復(fù)雜問題簡單化是本事,任重而道遠柏靶。希望大家可以在日常開發(fā)中運用好這種簡潔高效的技術(shù)弃理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市屎蜓,隨后出現(xiàn)的幾起案子痘昌,更是在濱河造成了極大的恐慌,老刑警劉巖炬转,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辆苔,死亡現(xiàn)場離奇詭異,居然都是意外死亡扼劈,警方通過查閱死者的電腦和手機驻啤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荐吵,“玉大人骑冗,你說我怎么就攤上這事赊瞬。” “怎么了贼涩?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵森逮,是天一觀的道長。 經(jīng)常有香客問我磁携,道長褒侧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任谊迄,我火速辦了婚禮闷供,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘统诺。我一直安慰自己歪脏,他們只是感情好,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布粮呢。 她就那樣靜靜地躺著婿失,像睡著了一般。 火紅的嫁衣襯著肌膚如雪啄寡。 梳的紋絲不亂的頭發(fā)上豪硅,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機與錄音挺物,去河邊找鬼懒浮。 笑死,一個胖子當著我的面吹牛识藤,可吹牛的內(nèi)容都是我干的砚著。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼痴昧,長吁一口氣:“原來是場噩夢啊……” “哼稽穆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起赶撰,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤舌镶,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后扣囊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乎折,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年侵歇,在試婚紗的時候發(fā)現(xiàn)自己被綠了骂澄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡惕虑,死狀恐怖坟冲,靈堂內(nèi)的尸體忽然破棺而出磨镶,到底是詐尸還是另有隱情,我是刑警寧澤健提,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布琳猫,位于F島的核電站,受9級特大地震影響私痹,放射性物質(zhì)發(fā)生泄漏脐嫂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一紊遵、第九天 我趴在偏房一處隱蔽的房頂上張望账千。 院中可真熱鬧,春花似錦暗膜、人聲如沸匀奏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽娃善。三九已至,卻和暖如春瑞佩,著一層夾襖步出監(jiān)牢的瞬間聚磺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工钉凌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留咧最,地道東北人。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓御雕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親滥搭。 傳聞我的和親對象是個殘疾皇子酸纲,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

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

  • 簡介 測試目的:模擬多種可能性,減少錯誤瑟匆,增強健壯性闽坡,提高穩(wěn)定性。 測試種類:在iOS中的通常分為單元測試和UI測...
    i順頌時宜閱讀 9,107評論 0 39
  • 單元測試不是一個小工程愁溜,需要多用些時間才能做好疾嗅,不要希望通過這個文章就能掌握單元測試,這只是一個入門冕象,需要自己動手...
    勇不言棄92閱讀 7,795評論 9 60
  • 1. 單元測試入門——優(yōu)秀基因 單元測試最初興起于敏捷社區(qū)代承。1997年,設(shè)計模式四巨頭之一Erich Gamma和...
    厲鉚兄閱讀 2,648評論 3 16
  • 前言: 對于單元測試來說渐扮,我想大部分同行论悴,在項目中掖棉,很少會用到,也有一大部分膀估,知道單元測試這個東西幔亥,但是確切的說沒...
    麥穗0615閱讀 3,217評論 4 44
  • 有時候往往需要我們?nèi)ヅ袛嚯娫捥柎a輸入的格式是否正確 下面一個簡單的方法就可以解決這個問題: #pragma mar...
    16哥哥閱讀 618評論 0 0