一、簡介
單元測試(Unit Testing)
是一種軟件測試方法殖卑,主要用于確定各個獨立的軟件模塊是否正確孵稽。在這個過程中菩鲜,開發(fā)者會對函數(shù)睦袖、程序或方法進行詳細的檢查以確保它們能夠按預(yù)期運行馅笙。
單元測試通常由軟件開發(fā)人員自己編寫,他們將確認具體功能是否按照設(shè)計要求正常工作皿淋。單元測試的目標是隔離代碼的每個部分窝趣,并確保每個獨立的部分都能正常工作哑舒。
例如洗鸵,如果你有一個計算器應(yīng)用程序膘滨,你可能會為加法火邓、減法蹈矮、乘法和除法等每個功能編寫單元測試,以確保當給定特定輸入時踊东,這些功能能返回正確的結(jié)果刚操。
這種測試方法可以幫助找出代碼中的錯誤和問題闸翅,確保在整個軟件系統(tǒng)中的各個單位都能正常工作措近,從而提高了代碼質(zhì)量和可維護性兼蜈。
在 iOS 開發(fā)中 XCTest
是 Apple
提供的官方測試框架蔽午,用于進行單元測試父能、性能測試以及用戶界面測試碳抄。
以下是關(guān)于 XCTest
的詳細介紹:
- 主要組件:
XCTest
框架包括XCTests
(單元測試)痊夭、XCUItests
(用戶界面測試)和XCPPerformanceTests
(性能測試)旱捧。- 單元測試(
XCTests
):這是最基礎(chǔ)的測試類型构捡,主要用于測試應(yīng)用中的個別方法或計算邏輯是否按預(yù)期工作液南。- 用戶界面測試(
XCUItests
):這種測試模擬用戶與應(yīng)用程序的交互操作,例如點擊按鈕勾徽、滑動屏幕等滑凉。該測試可確保當用戶使用您的應(yīng)用時,界面和交互功能能正常運行喘帚。- 性能測試(
XCPPerformanceTests
):這種測試幫助您量化代碼的性能畅姊,并在代碼更改后跟蹤其變化。您可以為某些任務(wù)設(shè)置基準時間吹由,然后在優(yōu)化代碼后比較新的執(zhí)行時間若未。- 測試斷言:
XCTest
提供了一套斷言供你驗證測試結(jié)果。這些斷言包括XCTAssertTrue()
,XCTAssertFalse()
,XCTAssertEqual()
等溉知。- 集成和運行:
XCTest
完全集成在Xcode
中陨瘩,且易于使用腕够。你可以直接從Xcode
的測試導(dǎo)航器運行測試,或者使用快捷鍵Cmd + U
運行所有測試舌劳。- 測試報告:
Xcode
會為執(zhí)行的XCTest
測試生成詳細的測試報告帚湘,包括每個測試的運行時間以及哪些測試通過了,哪些失敗了甚淡。
總的來說大诸,XCTest
是一個強大的工具,能夠幫助 iOS 開發(fā)者確保他們的應(yīng)用在各種情況下都可以正常工作贯卦。
二资柔、如何項目中添加 XCTest
在 Xcode
中添加一個新的 XCTest
單元測試模塊相對簡單。下面是具體的步驟:
1撵割、創(chuàng)建項目是直接創(chuàng)建
創(chuàng)建項目成功過項目目錄下即可看到對應(yīng)的單元測試文件夾
帶有 Tests 后綴的文件夾
2贿堰、已有的項目添加 XCTest
3、運行 - (void)testExample
方法一直報錯 Test Failed
啡彬。
報錯 The bundle “...Tests” couldn’t be loaded. Try reinstalling the bundle.
需要設(shè)置 team 和 項目的 team 一致來解決
4羹与、'XCTest/XCTest.h' file not found
問題描述:fatal error: 'XCTest/XCTest.h' file not found
解決方法
在報錯的
Target
中的Building settings
中FRAMEWORK_SEARCH_PATHS
添加$(PLATFORM_DIR)/Developer/Library/Frameworks
5、運行項目報錯 Library not loaded: @rpath/XCTest.framework/XCTest
三庶灿、 XCTest
簡單使用
方法簡單概述:
setUp
方法是當前測試類的初始化方法纵搁,我們可以將一些資源準備工作在這個方法中完成;
tearDown
方式在測試結(jié)束后會調(diào)用往踢,用來進行資源的清理腾誉。
測試函數(shù)都需要以text
開頭,testExample
是默認生成的一個測試用例函數(shù)峻呕,可以編輯testXXX
方法利职,只要是test
開頭的方法都可以進行檢查。xCTest
框架提供了眾多測試斷言山上,可以用來測試眼耀,命中斷言,則認為當前測試用例失敗佩憾。
testPerformanceExample
是性能測試的一個案例哮伟,其內(nèi)的measureBlock
里的代碼會被默認執(zhí)行10次,最終輸出每次執(zhí)行的時間消耗報告妄帘。
示例
1.測試函數(shù)的要求是:1.必須無返回值楞黄;2.實例方法要求:沒有入?yún)ⅲ瑳]有返回值抡驼,以 test
開頭鬼廓。
2.測試函數(shù)執(zhí)行的順序:以函數(shù)名中 test
后面的字符大小有關(guān),比如 -(void)test001XXX
會先于 -(void)test002XXX
執(zhí)行致盟;
3.運行單元測試的快捷鍵:CMD + U
;
4.下面一共18個斷言(SDK中也是18個碎税,其含義轉(zhuǎn)自 ios UnitTest 學習筆記尤慰,真心佩服原文的博主):
`XCTFail(format…)` 生成一個失敗的測試;
`XCTAssertNil(a1, format...)` 為空判斷雷蹂,a1為空時通過伟端,反之不通過;
`XCTAssertNotNil(a1, format…)` 不為空判斷匪煌,a1不為空時通過责蝠,反之不通過;
`XCTAssert(expression, format...)` 當 `expression` 求值為 `TRUE` 時通過萎庭;
`XCTAssertTrue(expression, format...)` 當 `expression` 求值為 `TRUE` 時通過霜医;
`XCTAssertFalse(expression, format...)` 當 `expression` 求值為 `False` 時通過;
`XCTAssertEqualObjects(a1, a2, format...)` 判斷相等驳规, `[a1 isEqual:a2]` 值為TRUE時通過肴敛,其中一個不為空時,不通過达舒;
`XCTAssertNotEqualObjects(a1, a2, format...)` 判斷不等值朋, `[a1 isEqual:a2]` 值為 `False` 時通過;
`XCTAssertEqual(a1, a2, format...)` 判斷相等(當a1和a2是 C語言標量巩搏、結(jié)構(gòu)體或聯(lián)合體時使用,實際測試發(fā)現(xiàn) `NSString` 也可以);
`XCTAssertNotEqual(a1, a2, format...)` 判斷不等(當a1和a2是 C語言標量趾代、結(jié)構(gòu)體或聯(lián)合體時使用)贯底;
`XCTAssertEqualWithAccuracy(a1, a2, accuracy, format...)` 判斷相等,(`double`或`float`類型)提供一個誤差范圍撒强,當在誤差范圍(`+/-accuracy`)以內(nèi)相等時通過測試禽捆;
`XCTAssertNotEqualWithAccuracy(a1, a2, accuracy, format...)` 判斷不等,(`double`或`float`類型)提供一個誤差范圍飘哨,當在誤差范圍以內(nèi)不等時通過測試胚想;
`XCTAssertThrows(expression, format...)` 異常測試,當 `expression` 發(fā)生異常時通過芽隆;反之不通過浊服;(很變態(tài))
`XCTAssertThrowsSpecific(expression, specificException, format...)` 異常測試,當 `expression` 發(fā)生 `specificException` 異常時通過胚吁;反之發(fā)生其他異逞捞桑或不發(fā)生異常均不通過;
`XCTAssertThrowsSpecificNamed(expression, specificException, exception_name, format...)` 異常測試腕扶,當 `expression` 發(fā)生具體異常孽拷、具體異常名稱的異常時通過測試,反之不通過半抱;
`XCTAssertNoThrow(expression, format…)` 異常測試脓恕,當 `expression` 沒有發(fā)生異常時通過測試膜宋;
`XCTAssertNoThrowSpecific(expression, specificException, format...)` 異常測試,當 `expression` 沒有發(fā)生具體異常炼幔、具體異常名稱的異常時通過測試秋茫,反之不通過;
`XCTAssertNoThrowSpecificNamed(expression, specificException, exception_name, format...)` 異常測試江掩,當 `expression` 沒有發(fā)生具體異常学辱、具體異常名稱的異常時通過測試,反之不通過
特別注意下 XCTAssertEqualObjects
和 XCTAssertEqual
环形。
`XCTAssertEqualObjects(a1, a2, format...)` 的判斷條件是 `[a1 isEqual:a2]` 是否返回一個 `YES`策泣。
`XCTAssertEqual(a1, a2, format...)` 的判斷條件是`a1 == a2`是否返回一個 `YES`。
對于后者抬吟,如果a1和a2都是基本數(shù)據(jù)類型變量萨咕,那么只有a1 == a2
才會返回YES
。例如下面代碼中只有第二行可以通過測試:
// 1.比較基本數(shù)據(jù)類型變量
XCTAssertEqual(1, 2, @"a1 = a2 shoud be true"); // 無法通過測試
XCTAssertEqual(1, 1, @"a1 = a2 shoud be true"); // 通過測試
但是火本,如果a1和a2都是指針危队,那么只有a1和a2指向同一個對象才會返回YES
。例如下面的代碼中:
// 3.比較NSArray對象
NSArray *array1 = @[@1];
NSArray *array2 = @[@1];
NSArray *array3 = array1;
XCTAssertEqual(array1, array2, @"a1 and a2 should point to the same object"); // 無法通過測試
XCTAssertEqual(array1, array3, @"a1 and a2 should point to the same object"); // 通過測試
array1和array2指向不同對象钙畔,無法通過測試茫陆。
這里比較奇怪的是,NSString
另當別論:
// 2.比較NSString對象
NSString *str1 = @"1";
NSString *str2 = @"1";
NSString *str3 = str1;
XCTAssertEqual(str1, str2, @"a1 and a2 should point to the same object"); // 通過測試
XCTAssertEqual(str1, str3, @"a1 and a2 should point to the same object"); // 通過測試
盡管str1和str2指向不同的對象擎析,但是二者的指針比較卻能通過測試簿盅。
由于str1和str2指向同一常量,常量在內(nèi)存的 data
段中地址是固定的揍魂,所以二者地址相同桨醋。
四、異步函數(shù)的測試
前面我們演示的測試用例所執(zhí)行的邏輯都是同步的现斋,但在實際的項目中喜最,異步的操作很多,XCTest
框架中也提供了異步邏輯的測試方式庄蹋。例如對如下業(yè)務(wù)方法進行測試:
- (void)requestData:(void (^)(BOOL))complete {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (complete) {
complete(YES);
}
});
}
測試用例如下:
- (void)testAsync {
XCTestExpectation *except = [self expectationWithDescription:@"異步請求測試用例"];
[self.viewModel requestData:^(BOOL success) {
XCTAssertTrue(success);
[except fulfill];
}];
[self waitForExpectationsWithTimeout:10 handler:^(NSError * _Nullable error) {
}];
}
XCTestExpectation
可以理解為一個期望對象瞬内,當使用此對象調(diào)用 fulfill
方法后,表示異步邏輯完成蔓肯。
五遂鹊、代碼覆蓋率
與單元測試相關(guān)的,還有一個重要的概念:代碼覆蓋率蔗包。代碼覆蓋率是指在整個測試執(zhí)行過程中秉扑,覆蓋到的功能函數(shù)與所有功能函數(shù)的比例。覆蓋率越高說明測試涉及的功能越全。
測試完成后舟陆,可以直接在Xcode
中查看代碼覆蓋率误澳,如下圖所示:
單元測試保持較高的覆蓋率是非常重要的,其從另一個方面也是測試質(zhì)量的保障秦躯。【當然這里是一個示例忆谓,所以沒有很高的覆蓋率】
如果看不到代碼覆蓋率,點擊edit scheme
,看對應(yīng)的tagart-->test-->code Coverage
那個對勾都勾上了沒有
六踱承、關(guān)于單元測試(邏輯代碼部分)的幾點建議
不涉及
UI
方面的自動化測試倡缠,只針對邏輯代碼的單元測試,下面這些建議可供參考:
在編碼時茎活,要盡量按照
MVVM
的模式進行開發(fā)昙沦,相比MVC
模式,MVVM
的邏輯代碼都封裝在VM
里面载荔,更利于進行脫離UI
的測試盾饮。可以設(shè)想懒熙,如果將邏輯方法都寫在View
或ViewController
中丘损,則執(zhí)行測試用例時就不得不引入很多額外的頁面UI
組件。編寫測試用例時工扎,有3個核心要考慮的點徘钥,即輸入,輸出和結(jié)果判定肢娘。我們通過輸入來設(shè)置測試用例的初始狀態(tài)吏饿,通過對輸出的結(jié)果判定來決定測試用例是否通過。
在開發(fā)中蔬浙,編寫的函數(shù)要盡量符合下面的特性:功能單一,有輸入有輸出贞远。
函數(shù)有輸入?yún)?shù)畴博,沒有返回值時,需要對輸入的參數(shù)進行修改蓝仲,則這種場景編寫測試用例時俱病,要判斷的是執(zhí)行函數(shù)操作后的原始變量是否符合預(yù)期。
函數(shù)沒有輸入?yún)?shù)袱结,沒有返回值時亮隙,其作用只是執(zhí)行一段邏輯操作,例如存儲文件垢夹,修改文件等溢吻。這時我們可以修改下功能函數(shù),在函數(shù)內(nèi)返回操作成功或失敗的結(jié)果,測試用例使用此結(jié)果來作為是否通過的標準促王。
七犀盟、XCTest
框架中也是有 UI
測試的
例如: 創(chuàng)建一個 UI Test Target
自帶會有這樣的方法測試啟動時長性能方法
生成文件示例
image.png
運行查看啟動時長
啟動時長日志輸出
總結(jié):默認會聯(lián)系多次啟動,可以查看每次的時長蝇狼,方便對比結(jié)果阅畴。
遇到的 UITest
問題
testExample: Device is not configured for UI testing - use of XCUIApplication is not supported. This can happen when XCUIApplication is used in a unit test bundle instead of a UI test bundle. (NSInternalInconsistencyException)
解決方案: 1、UI test 和 邏輯 test 是不同的 target迅耘, 可能選擇錯了贱枣,需要創(chuàng)建 UI test target
更多的使用可以參考iOS單元測試的那些事兒