iOS - 單元測試

簡單使用單元測試

對方法引用AFN框架的單元測試

不寫單元測試的程序員是不合格的,為了讓自己成為一名合格的程序員,學習如何寫單元測試是很有必要的,這里以Xcode集成的測試框架XCTest為例。

XCTest基礎用法

默認的測試類繼承自XCTestCase,當然也可以自定義測試類,添加一些公共的輔助方法,所有的測試方法都必須以test開頭,且不能有參數,不然不會識別為測試方法。

@interface__Tests:XCTestCase@end@implementation__Tests// 在每一個測試用例開始前調用,用來初始化相關數據- (void)setUp {? ? [supersetUp];// 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.[supertearDown];}// 測試方法- (void)testExample {// This is an example of a functional test case.// Use XCTAssert and related functions to verify your tests produce the correct results.}// 性能測試方法,通過測試block中方法執行的時間,比對設定的標準值和偏差覺得是否可以通過測試- (void)testPerformanceExample {// This is an example of a performance test case.[selfmeasureBlock:^{// Put the code you want to measure the time of here.}];}/// 我自定義的 針對 Person 類的測試方法- (void)testPerson{}@end

斷言

XCTest的斷言具體可查閱XCTestAssertions.h文件,這里還是做個簡單的總結

//通用斷言XCTAssert(expression,format...)//常用斷言:XCTAssertTrue(expression,format...)XCTAssertFalse(expression,format...)XCTAssertEqual(expression1, expression2,format...)XCTAssertNotEqual(expression1, expression2,format...)XCTAssertEqualWithAccuracy(expression1, expression2, accuracy,format...)XCTAssertNotEqualWithAccuracy(expression1, expression2, accuracy,format...)XCTAssertNil(expression,format...)XCTAssertNotNil(expression,format...)

舉個例子

- (void)testExample {//設置變量和設置預期值NSUIntegera =10;NSUIntegerb =15;NSUIntegerexpected =24;//執行方法得到實際值NSUIntegeractual = [selfadd:a b:b];//斷言判定實際值和預期是否符合XCTAssertEqual(expected, actual,@"add方法錯誤!");}-(NSUInteger)add:(NSUInteger)a b:(NSUInteger)b{returna+b;}

從這也能看出一個測試用例比較規范的寫法,1:定義變量和預期,2:執行方法得到實際值,3:斷言

當然在有些特殊情況下直接使用這些斷言,會讓代碼看起來很臃腫,使用時可以對其進行一定的封裝一下:

#define ml_ssertTrue(expr)XCTAssertTrue((expr), @"設置指定的字符串,同下")#define ml_assertFalse(expr)XCTAssertFalse((expr), @"")#define ml_assertNil(a1)XCTAssertNil((a1), @"")#define ml_assertNotNil(a1)XCTAssertNotNil((a1), @"")#define ml_assertEqual(a1, a2)XCTAssertEqual((a1),(a2), @"")#define ml_assertEqualObjects(a1, a2)XCTAssertEqualObjects((a1),(a2), @"")#define ml_assertNotEqual(a1, a2)XCTAssertNotEqual((a1),(a2), @"")#define ml_assertNotEqualObjects(a1, a2)XCTAssertNotEqualObjects((a1),(a2), @"")#define ml_assertAccuracy(a1, a2, acc)XCTAssertEqualWithAccuracy((a1),(a2),(acc))

簡單實用準備工作

795875-a207ce29f12fa7f2.png.jpeg

如果是之前創建的項目,里面沒有勾選,可以自己創建:

795875-9e3ebd787a699757.png.jpeg

進入到這個類,

setUp是每個測試方法調用執行,

tearDown是每個測試方法調用執行。

testExample是測試方法,和我們新建的沒有差別。

不過測試方法必須testXXX的格式,且不能有參數,不然不會識別為測試方法。測試方法的執行順序是字典序排序。

按快捷鍵Command + U進行單元測試,這個快捷鍵是全部測試。

795875-362afa43ea505bb6.png.jpeg

一、簡單使用單元測試

1、創建一個Person類,里面有一個test1類方法

2、順便勾選右邊的測試單元按鈕,為了對這個.m文件的數據測試

795875-192f3a273594f9e7.png.jpeg

3、如果項目比較小,就可以直接在項目剛創建的tests方法里測試,如果項目比較大的話,就創建一個單獨的tests,例如:PersonTets:

795875-89721ddcf3d48789.png.jpeg

4、創建一個測試方法:testPerson:

795875-cbeeb6ba7a19c6ef.png.jpeg

上面也說了:測試方法必須testXXX的格式,且不能有參數,不然不會識別為測試方法

如果前面不出現小菱形,可以Build一下

5、點擊小菱形圖標,出現綠色說明成功:

795875-fdefc42918aaccb4.png.jpeg

6、有時候需要驗證值的話,就使用 XCTAssert(<#expression, ...#>),或者其他的斷言方法測試結果;

二、對方法引用AFN框架的單元測試

1、測試帶有網絡請求的方法,例如:

795875-12be468144d6e31a.png.jpeg

2、 按照之前的那樣 [Person test2]的話,點擊小菱角會出現找到AFN框架:

795875-d4d646284ee3dd06.png.jpeg

3、需要做一些配置

3.1、復制Target(App) - Build Setting - Header Search Paths 的路徑。

795875-27159a355bc9a14f.png.jpeg

3.2、粘貼到Target(UnitTests) - Build Setting - Header - Search Paths里。

3.3、復制Target(App) - Build Setting - User-Defined - PODS_ROOT整條。

3.4、到Target(UnitTests) - Build Setting - User-Defined新建一條PODS_ROOT。

795875-601bbddba0354ed1.png.jpeg

4、OK了!

三、期望

期望實際上是異步測試,當測試異步方法時,因為結果并不是立刻獲得,所以我們可以設置一個期望,期望是有時間限定的的,fulfill表示滿足期望。

例如

- (void)testAsynExample {// 1. 創建期望XCTestExpectation *exp = [selfexpectationWithDescription:@"這里可以是操作出錯的原因描述。。。"];NSOperationQueue*queue = [[NSOperationQueuealloc]init];? ? [queue addOperationWithBlock:^{//模擬:這個異步操作需要2秒后才能獲取結果,比如一個異步網絡請求sleep(2);//模擬:獲取的異步操作后,獲取結果,判斷異步方法的結果是否正確XCTAssertEqual(@"a",@"a");//2. 如果斷言沒問題,就調用fulfill宣布測試滿足[exp fulfill];? ? }];//3. 設置延遲多少秒后,如果沒有滿足測試條件就報錯[selfwaitForExpectationsWithTimeout:3handler:^(NSError* _Nullable error) {if(error) {NSLog(@"Timeout Error: %@", error);? ? ? ? }? ? }];}

這個測試肯定是通過的,因為設置延遲為3秒,而異步操作2秒就除了一個正確的結果,并宣布了條件滿足 [exp fulfill],但是當我們把延遲改成1秒,這個測試用例就不會成功,錯誤原因是 expectationWithDescription:@"這里可以是操作出錯的原因描述。。。

異步測試除了使用expectationWithDescription

以外,還可以使用expectationForPredicate和expectationForNotification

,

掌握expectationWithDescription

即可,后兩者者僅僅了解

下面這個例子使用expectationForPredicate 測試方法,代碼來自于AFNetworking,用于測試backgroundImageForState方法

- (void)testThatBackgroundImageChanges {? ? XCTAssertNil([self.buttonbackgroundImageForState:UIControlStateNormal]);NSPredicate*predicate = [NSPredicatepredicateWithBlock:^BOOL(UIButton* _Nonnull button,NSDictionary * _Nullable bindings) {return[button backgroundImageForState:UIControlStateNormal] !=nil;? ? }];? ? [selfexpectationForPredicate:predicate? ? ? ? ? ? ? evaluatedWithObject:self.buttonhandler:nil];? ? [selfwaitForExpectationsWithTimeout:20handler:nil];}

利用謂詞計算,button是否正確的獲得了backgroundImage,如果正確20秒內正確獲得則通過測試,否則失敗。

expectationForNotification 方法 ,該方法監聽一個通知,如果在規定時間內正確收到通知則測試通過。

- (void)testAsynExample1 {? ? [selfexpectationForNotification:(@"監聽通知的名稱xxx") object:nilhandler:nil];? ? [[NSNotificationCenterdefaultCenter]postNotificationName:@"監聽通知的名稱xxx"object:nil];//設置延遲多少秒后,如果沒有滿足測試條件就報錯[selfwaitForExpectationsWithTimeout:3handler:nil];}

這個例子也可以用expectationWithDescription實現,只是多些很多代碼而已,但是這個可以幫助你更好的理解expectationForNotification 方法和 expectationWithDescription 的區別。同理,expectationForPredicate方法也可以使用expectationWithDescription實現。

functestAsynExample1(){letexpectation = expectationWithDescription("監聽通知的名稱xxx")letsub =NSNotificationCenter.defaultCenter().addObserverForName("監聽通知的名稱xxx", object:nil, queue:nil) { (not) ->Voidinexpectation.fulfill()? ? }NSNotificationCenter.defaultCenter().postNotificationName("監聽通知的名稱xxx", object:nil)? ? waitForExpectationsWithTimeout(1, handler:nil)NSNotificationCenter.defaultCenter().removeObserver(sub)}

四、期望實戰:

-(void)testRequest{# 創建期望XCTestExpectation *expectation =[selfexpectationWithDescription:@"沒有滿足期望"];? ? AFHTTPSessionManager *sessionManager = [AFHTTPSessionManager manager];? ? sessionManager.responseSerializer= [AFHTTPResponseSerializer serializer];? ? [sessionManager GET:@"http://www.weather.com.cn/adat/sk/101110101.html"parameters:nilsuccess:^(NSURLSessionDataTask* _Nonnull task,id_Nullable responseObject) {NSLog(@"responseObject:%@", [NSJSONSerializationJSONObjectWithData:responseObject options:1error:nil]);# 不為nil 通過,XCTAssertNotNil(responseObject,@"返回出錯");# 滿意[expectation fulfill];? ? } failure:^(NSURLSessionDataTask* _Nullable task,NSError* _Nonnull error) {# 為nil 通過,XCTAssertNil(error,@"請求出錯");? ? }];# 設置5秒的超時時間[selfwaitForExpectationsWithTimeout:3.0handler:^(NSError*error) {if(error) {NSLog(@"Timeout Error: %@", error);? ? ? ? }? ? }];}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 程序員最討厭的就是反復的修復查找BUG,,,,,特別是萬惡的IE6,不過沒辦法,改修復還是修復處理,誰讓我們干的是...
    虛幻的銹色閱讀 343評論 0 1
  • 前言 每次線上都會有些崩潰記錄在友盟,但是這些崩潰日志并不好查找崩潰的原因,我就在想有沒有好一點的方法. 錯誤的異...
    Link913閱讀 779評論 2 3
  • bug分類: 文案展示問題 數據展示問題 操作邏輯錯誤 崩潰問題 20號
    George_Luofz閱讀 189評論 0 1
  • 戳花鎮情感,關注最有情感深度的公眾號 我一直聲稱我喜歡大叔型的男人。氣質成熟,有品味,有一定的經濟基礎,為人處事拿...
    花鎮閱讀 2,530評論 0 0
  • 他是你的全世界,而我希望你的世界里能有自己的立錐之地。一個人最堅實的依靠從來不是男人的肩膀,而是自己的懷抱。 -1...
    慢小小閱讀 510評論 1 2