【SDK】XCTest 編寫過程中的一些問題和技巧

重要!

  1. 對于即將新增的功能函數,一定要先寫UT再寫實現代碼。因為這樣可以驗收代碼設計的角度避免一些思路問題。這一點很重要,也很好理解,資料很多就不贅述了。
  2. 對于已有的代碼,后面補UT Case的過程其實相當于code review,也是一個代碼優化的過程。雖然可能會覺得枯燥且無用功,但是寫完或者在寫的過程中會有很多收獲的,就如這篇文章記錄了我的收獲一樣。
    一個很好用的網站,不用謝

問題和技巧


  • 用斷言來表達值得的范圍
//如網絡狀態的返回值是枚舉,枚舉的值有{-1,1,2,3,4},所以返回值必須小于4且不等于0
    NSInteger netClassNotContainWifi = [Reachability getMobileNetworkClass];
    XCTAssertNotEqual(0, netClassNotContainWifi,@"返回值必須小于4且不等于0");
    XCTAssertLessThanOrEqual(netClassNotContainWifi, 4, @"返回值必須小于4且不等于0");

  • 可以用OCMock 來模擬一些測試環境或者比較難給出的輸入
//此處為mock 的數據,直接搜OCMock
    NSInteger fd = [Reachability create3GOr4GFD];
    XCTAssertFalse(fd != -1, @"創建");//simulator use WiFi ,so the case must be failed


  • 對于一些基本的工具類,不涉及邏輯的其實可以不用寫UT
    如下面的代碼是對獲取時間類的方法。里面的實現不怎么涉及到邏輯,只是簡單的函數封裝。
    原則上是要寫的(如果時間允許還是要寫的,誰知道誰會在哪個時間不小心改了啥呢,畢竟UT是用來干這個事情的),我寫了,寫完覺得沒必要,寫這個帖子的時候又覺得很有必要,哈哈哈。
//被測試代碼如下兩個方法
+ (long)getUnixTime {
    return [[self class] getUnixTime:[NSDate date]];
}

+ (long)getUnixTime:(NSDate *)date {
    long time;
    NSDate *fromdate= date;
    time=(long)[fromdate timeIntervalSince1970];
    return time;
}
#import <XCTest/XCTest.h>
#import "DLNSDateUtil.h"

@interface DLNSDateUtilTests : XCTestCase

@end

@implementation DLNSDateUtilTests

- (void)testExample {

    long unixTime = [DLNSDateUtil getUnixTime];
    sleep(1);
    long unixTimeNow = [DLNSDateUtil getUnixTime:[NSDate date]];
    
    XCTAssertTrue((unixTimeNow > unixTime), @"感覺這個類不需要測試");
}

  • 遇到相同功能但是函數名不同的已有代碼,或者命名不合理的,果斷修改
    比如上面DLNSDateUtil類中,getUnixTime一般默認返回當前時間,但是原有代碼還有一個方法是getUnixTimeNow如下
+ (long)getUnixTimeNow {
    return [[self class] getUnixTime];
}

真不知道原來的人怎么想的,果斷刪除getUnixTimeNow


  • 遇到已有代碼中(后寫UT的場景),有些功能函數整個項目中壓根就沒有使用到的方法,是否需要寫對應的UT呢
    舉個??:比如一個文件讀寫類,對外暴露了增刪改查四個功能,項目中此時只用到其中的增刪改三個,查詢方法暫時沒有用到,那么這個時候有2種處理方式:
  1. 直接刪除這個沒有用到的方法
  2. 繼續寫UT

這里我的思路是這樣的:對于SOLID里的S原則來說,文件讀寫類必須有增刪改查四個功能,即使此時用不到,如果少了查詢功能,那么你的功能函數是不完整的。一個函數干一個事,一個功能必須四個干事的函數。你現在用不到,可能你的同時負責的功能能用到呢,或者你以后能用到呢。所以這里是要寫UT的。


  • 遇到壓根就不用的類怎么辦(可能以前用,現在不用了。或者從來就沒有用過)
    其實這個就不是UT的范疇了,這里和同事商量了下:在寫UT初期,先跳過這樣的類。加個TODO標記下。

我一共嘗試了兩種方式

  1. 從業務角度出發:
    從你業務最開始的邏輯部分開始,一層層剝離開,然后去到那個單一功能類,比如文件管理類,然后針對該類的函數寫UT Case

2.從代碼結構出發,一般項目都會有Utils Handler Http這樣的底層代碼文件夾。直接在UT文件夾下建立類似的文件目錄,方便管理測試代碼。而不是UT demo 工程那么隨意放置。


4601578974308_.pic.jpg
第一種不可取,因為業務邏輯沒個定型,剛開始還好,然后看著看著就不知道去哪里了,迷失了自我。第二種則思路比較清晰,也能客觀的看這個類的封裝及功能設計是否合理。

對于一個獲取IP 的類有下面三個函數,很常規的吧

+ (NSString *)getWifiIPAddress;
+ (NSString *)getCellularIPAddress;
+ (NSMutableArray *)resolveDomainName:(NSString *)domainName;
@implementation NetworkAddressTests
- (void)testExample {
   
    NSString *wifiIP = [NetworkAddress getWifiIPAddress];
    NSString *cellularIP = [NetworkAddress getCellularIPAddress];
    
    XCTAssertGreaterThanOrEqual(wifiIP.length, 10);
    XCTAssertNil(cellularIP,@"simulator without cellular");
    
    NSMutableArray *name = [NetworkAddress resolveDomainName:@"hhtps://www.baidu.com"];
    NSLog(@"ssss%@", name);
    
}

@end

然后我就寫出了如上的測試case,然后跑完發現這個類的coverage 很高,高達77%
很爽,但是!!!!!!


30051578985283_.pic.jpg

這樣的case 毫無意義,因為即使這個時候這些個方法在某個時刻被改變了,函數完全不是預期的了,這樣的case 并不能檢查出來問題。所以,一定要設置好固定的預期值,然后大部分(不要求全部)正常異常case都要有,這樣的函數級別的UT才有意義,不然即使覆蓋率高達100%也毫無意義。

這里就要說怎么寫了:比如創建文件夾函數,就有正常的創建成功,創建非法路徑和創建已經存在的路徑這三個常規case,因為創建文件夾函數有返回布爾值,所以直接拿來斷言就好。

    NSString *pathname = @"md5";
    NSString *errorPathname = @"";
    
    BOOL creatFolderPath = [FileUtil createFolder:pathname];
    XCTAssertTrue(creatFolderPath,@"create folder");
    
    BOOL creatErrorFolderPath = [FileUtil createFolder:errorPathname];
    XCTAssertFalse(creatErrorFolderPath,@"create folder");
    
    BOOL creatExistsFolderPath = [FileUtil createFolder:pathname];
    XCTAssertTrue(creatExistsFolderPath,@"create folder");

所以,case不僅僅是讓代碼在XCTest中被執行一邊,而是具有校驗意義的。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容