iOS-細(xì)說(shuō)單元測(cè)試(上)

  • 什么是單元測(cè)試

    單元測(cè)試,就是測(cè)試代碼"單元"的功能,以確保在任何可能的條件下達(dá)到預(yù)期目的的一種測(cè)試方法.單元(Unit)是代碼中一個(gè)可測(cè)試的邏輯部分. 單元測(cè)試可以幫助開發(fā)者找到錯(cuò)誤和崩潰原因,這也是蘋果拒絕上架的首要原因(crash了還上個(gè)毛架..)!

測(cè)試方法應(yīng)該要能夠響應(yīng)所有類型的輸入,包括有效輸入和無(wú)效輸入的情況,以確保單元能夠正常運(yùn)行,可以這么理解,在正常操作下要能獲得我想要的結(jié)果,在異常情況(空值,缺少參數(shù)..)等一些條件下不反回對(duì)象,甚至能夠?qū)﹀e(cuò)誤進(jìn)行處理和回應(yīng).無(wú)論開發(fā)者對(duì)單元進(jìn)行了什么更改,現(xiàn)有的測(cè)試方法都應(yīng)該能夠成功運(yùn)行,而新加的測(cè)試也應(yīng)該要成功運(yùn)行.所以測(cè)試方法很關(guān)鍵!

But.很多開發(fā)者對(duì)單元測(cè)試不太感興趣,雖然它十分整潔,可以驗(yàn)證許許多多的問(wèn)題,但是創(chuàng)建,維護(hù)卻需要花時(shí)間.如果要覆蓋所有的功能和使用場(chǎng)景的話.意味著投入精力會(huì)更多一點(diǎn).
小結(jié): 測(cè)試可以增加項(xiàng)目的穩(wěn)定性,減少錯(cuò)誤的發(fā)生.一個(gè)良好的測(cè)試可以極高地提升用戶的滿意度.


  • 測(cè)試基礎(chǔ)概念
    Xcode使用XCTest作為單元測(cè)試框架,在Xcode5之前,使用的是一個(gè)名為OCUnit的開源測(cè)試框架. XCTest就是對(duì)OCUnit的替代品,能夠更好地與XCode協(xié)作.
    單元測(cè)試的概念中有四個(gè)層級(jí):
    1. 測(cè)試套件(Test suite): 測(cè)試套件是項(xiàng)目中所有測(cè)試的集合,在Xcode中,測(cè)試套件作為一個(gè)獨(dú)立的對(duì)象存在.
  1. 測(cè)試用類例(Test case classes): 測(cè)試功能是存放在類當(dāng)中的,每個(gè)測(cè)試的例類通常是對(duì)應(yīng)一個(gè)單獨(dú)類來(lái)進(jìn)行測(cè)試.比如: 對(duì)Login類的測(cè)試 ,應(yīng)該由LoginTests類來(lái)完成,所有的單元測(cè)試類都必須要繼承XCTestCase類.

  2. ** 測(cè)試用例方法():** 測(cè)試用例類包含多個(gè)方法,用來(lái)測(cè)試類的各種功能.

  3. 斷言(Assertions): 斷言用于檢查結(jié)果是否符合預(yù)期,如果不符合,斷言則會(huì)失敗,并拋出失敗的原因.(調(diào)試神器)

E58E1817-B9CF-473E-9F3C-F43D83A1BE51.png

創(chuàng)建項(xiàng)目的時(shí)候勾選 Include Unit Tests
成功創(chuàng)建之后,打開項(xiàng)目結(jié)構(gòu).系統(tǒng)幫我們生成了一個(gè) "項(xiàng)目名+Tests"的文件夾.已經(jīng)囊括了單元測(cè)試的 .m文件.

2EAA4ED8-4E0E-40A9-A6D8-968766AF7339.png

分析下 .m的結(jié)構(gòu)

<code>
#import <XCTest/XCTest.h>
#import "Person.h"

    @interface UnitTestTests : XCTestCase

    @end

    @implementation UnitTestTests

    - (void)setUp {
      [super setUp];
     // Put setup code here. This method is called before the invocation of each test method in the class.
    }

    - (void)tearDown {
      [super tearDown];
   
      // Put setup code here. This method is called before the invocation of each test method in the class.
    }

    - (void)testExample {

     }

    - (void)testPerformanceExample {

       [self measureBlock:^{
   
       }];
    }

</code>

說(shuō)說(shuō)這里面的幾個(gè)方法.

測(cè)試用例類 包含了 setUp 和 tearDown 方法, 這兩個(gè)方法不屬于測(cè)試用例方法,他們是測(cè)試用例類的初始化和析構(gòu)方法.setUp里面存放的是所有對(duì)象的設(shè)置代碼,而tearDown里面存放的是諸如關(guān)閉文件,取消網(wǎng)絡(luò)請(qǐng)求等清理活動(dòng)的代碼.Xcode會(huì)依次調(diào)用 setUp,某個(gè)測(cè)試用例方法和tearDown方法.如果有多個(gè)測(cè)試方法的話,那么setUp和tearDown會(huì)在每調(diào)用一次測(cè)試方法的過(guò)程中調(diào)用一次!
testExample是示例測(cè)試方法
testPerformanceExample是性能測(cè)試的示例方法.

對(duì)于Xcode來(lái)說(shuō),測(cè)試分兩種:

a. 功能測(cè)試 :功能不符合預(yù)期會(huì)報(bào)錯(cuò).

b. 性能測(cè)試 :性能測(cè)試需要設(shè)置基準(zhǔn)線,一旦發(fā)現(xiàn)測(cè)試結(jié)果低于基準(zhǔn)線,或者超出最大標(biāo)準(zhǔn)差(STDDEV)限制,就會(huì)報(bào)告一個(gè)錯(cuò)誤.
PS:這里就不對(duì)性能測(cè)試做其他說(shuō)明了,有興趣可以自行搜集資料.

注意: 自定義的測(cè)試方法必須以 test開始,這樣Xcode才能找到. (左邊有圖標(biāo)則表明方法有效)

Paste_Image.png

運(yùn)行測(cè)試

當(dāng)指向測(cè)試類和測(cè)試方法的時(shí)候,會(huì)出現(xiàn)一個(gè)按鈕,我們可以運(yùn)行所有的測(cè)試,也可以運(yùn)行某一個(gè)獨(dú)立的測(cè)試方法.測(cè)試結(jié)束后,Xcode將會(huì)返回成功或者是失敗結(jié)果

Paste_Image.png

當(dāng)然,我們也可以選中我們想要運(yùn)行的測(cè)試類,然后選擇菜單欄的 product->performAction -> Run Test Methods來(lái)運(yùn)行選中的測(cè)試類.如果你只選擇了一個(gè)測(cè)試類的話,那么相應(yīng)的選項(xiàng)會(huì)變成 Run "測(cè)試方法名".

Paste_Image.png

Xcode將自動(dòng)編譯并運(yùn)行應(yīng)用程序,然后執(zhí)行測(cè)試操作,測(cè)試操作完畢后,Xcode會(huì)退出應(yīng)用,短暫的顯示測(cè)試結(jié)果,同時(shí)測(cè)試結(jié)果會(huì)以相應(yīng)的圖標(biāo)表示測(cè)試是否成功!
在修改好我們發(fā)現(xiàn)的問(wèn)題之后,單擊失敗測(cè)試上的運(yùn)行按鈕,或者 選擇product->performAction -> TestAgian

回到我們的例子.

Paste_Image.png

這是.m的實(shí)現(xiàn)文件


Paste_Image.png

簡(jiǎn)單的給person賦值了一個(gè)name,age.

目前沒有對(duì)age做任何的判斷.意味著不論是給age 賦值任何 int類型的 值 都能夠成功!這當(dāng)然不符合我們的預(yù)期.

我們?cè)跍y(cè)試類里面可以開始動(dòng)我們的方法了.

    //  UnitTestTests.m
    //  UnitTestTests
    //
    //  Created by uncle-R on 16/7/5.
    //  Copyright ? 2016年 uncle-R. All rights reserved.
    //

    #import <XCTest/XCTest.h>
    #import "Person.h"

    @interface UnitTestTests : XCTestCase

    @property (nonatomic, strong) Person *p1;
    @end

    @implementation UnitTestTests

    - (void)setUp {
        [super setUp];
      //給p1.賦值一個(gè) -1的age;
        self.p1 = [[Person alloc]initWithName:@"小明" andAge:-1];
        // Put setup code here. This method is called before the invocation of each test method in the class.
    }

    - (void)tearDown {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        [super tearDown];
    }

    - (void)testAge{
        //我們想要的結(jié)果是年齡不管怎樣都必須大于0.
        XCTAssert(self.p1.age > 0 ,@"年齡必須大于0");

    }

    - (void)testExample {
        // This is an example of a functional test case.
        // Use XCTAssert and related functions to verify your tests produce the correct results.
        //[self ageTest];
    }

    - (void)testPerformanceExample {
        // This is an example of a performance test case.
        [self measureBlock:^{
            // Put the code you want to measure the time of here.
        }];
    }

結(jié)果如下

Paste_Image.png

可見測(cè)試結(jié)果失敗.我們必須在person類的修改.

   #import "Person.h"

    @implementation Person

    -(instancetype)initWithName:(NSString*)name andAge:(int)age{
        
        if (self = [super init]) {
            //對(duì)小于0的年齡做處理
            if(age <= 0) age = 1;
            self.age = age;
            
            self.name = name;
        }
        return self;
    }

    @end

在運(yùn)行一次測(cè)試.

Paste_Image.png

我們成功的通過(guò)了測(cè)試.


  • 再談功能測(cè)試

功能測(cè)試的核心在于 "斷言",上面的例子不難看出,測(cè)試結(jié)果取決于斷言是否成功.
功能測(cè)試大致可以分如下幾種:

1. 基礎(chǔ)測(cè)試
2. 布爾測(cè)試
3.相等測(cè)試
4.空值測(cè)試
5.無(wú)條件失敗
6.測(cè)試實(shí)例

斷言:

|測(cè)試名稱| 斷言 | 特性 |
|: ---- :|:------:| ---- ---:|
| 基礎(chǔ)測(cè)試 | XCTAssert | 最基礎(chǔ)的斷言,表達(dá)式為假則測(cè)試失敗|
| 布爾測(cè)試 |XCTAssertTure, XCTAssertFalse | 基礎(chǔ)測(cè)試的擴(kuò)展,當(dāng)表達(dá)式結(jié)果和布爾測(cè)試不匹配時(shí)則失敗.
| 相等測(cè)試 | XCTAsserEqual,XCTAssertEqualObjects等 | 兩個(gè)表達(dá)式不相等則測(cè)試失敗 |
|空值測(cè)試 |XCTAssertNotNil |如果表達(dá)式為空則測(cè)試失敗|
| 無(wú)條件失敗| XCTFail | 總是測(cè)試失敗(運(yùn)行過(guò)這段代碼就失敗)|

雖然所有的測(cè)試都可以使用XCTAssert,但是Xcode還是提供了一些更有效的宏.當(dāng)沒有更好代替的情況下,才使用XCTAssert

格式:
** XCTAssert(表達(dá)式,消息)**

注意一下, 無(wú)條件失敗通常用在控制流中,比如一個(gè)if esle結(jié)構(gòu)中.if 里面都是正常的結(jié)果,else 正常情況下壓根就不會(huì)進(jìn)去,我們這時(shí)候可以往else丟一個(gè) XCTFail進(jìn)去,立馬就能檢測(cè)出異常.

當(dāng)測(cè)試失敗的時(shí)候

在發(fā)現(xiàn)測(cè)試失敗的時(shí)候,先檢查自己測(cè)試方法是不是存在問(wèn)題,因?yàn)椴⒉皇撬羞壿嬕婚_始都想通的,因?yàn)橛行r(shí)候測(cè)試失敗并不僅僅是測(cè)試對(duì)象屬性為nil,或者方法出問(wèn)題,還可能是因?yàn)闇y(cè)試方法存在某些不達(dá)標(biāo)的地方.
因此在測(cè)試之前,最好先想清楚測(cè)試的目的,再檢查自己測(cè)試的過(guò)程是否符合要求.
tip: 當(dāng)斷言過(guò)多的時(shí)候要看仔細(xì), 一個(gè)Not 就能毀掉整個(gè)測(cè)試.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末羔砾,一起剝皮案震驚了整個(gè)濱河市蜒茄,隨后出現(xiàn)的幾起案子餐屎,更是在濱河造成了極大的恐慌腹缩,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件润讥,死亡現(xiàn)場(chǎng)離奇詭異楚殿,居然都是意外死亡竿痰,警方通過(guò)查閱死者的電腦和手機(jī)砌溺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門规伐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)猖闪,“玉大人肌厨,你說(shuō)我怎么就攤上這事柑爸。” “怎么了何址?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)胁镐。 經(jīng)常有香客問(wèn)我,道長(zhǎng)诸衔,這世上最難降的妖魔是什么盯漂? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮笨农,結(jié)果婚禮上就缆,老公的妹妹穿的比我還像新娘。我一直安慰自己谒亦,他們只是感情好竭宰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著份招,像睡著了一般切揭。 火紅的嫁衣襯著肌膚如雪锁摔。 梳的紋絲不亂的頭發(fā)上廓旬,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音谐腰,去河邊找鬼孕豹。 笑死涩盾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的巩步。 我是一名探鬼主播旁赊,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼椅野!你這毒婦竟也來(lái)了终畅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤竟闪,失蹤者是張志新(化名)和其女友劉穎离福,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炼蛤,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡妖爷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了理朋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片絮识。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嗽上,靈堂內(nèi)的尸體忽然破棺而出次舌,到底是詐尸還是另有隱情,我是刑警寧澤兽愤,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布彼念,位于F島的核電站,受9級(jí)特大地震影響浅萧,放射性物質(zhì)發(fā)生泄漏逐沙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一洼畅、第九天 我趴在偏房一處隱蔽的房頂上張望吩案。 院中可真熱鬧,春花似錦帝簇、人聲如沸徘郭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)崎岂。三九已至,卻和暖如春闪湾,著一層夾襖步出監(jiān)牢的瞬間冲甘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留江醇,地道東北人濒憋。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像陶夜,于是被迫代替她去往敵國(guó)和親凛驮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

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