前言
除了斷言,還有更多的測試。XCTest 什么時候創建和運行測試?iOS程序員特別容易對測試生命周期做出錯誤的假設。這些假設會導致測試設計中的錯誤。
比如我們經常遇到的,一個測試用例,在單獨運行的情況下可以測試通過,但是在項目組合測試中失敗,為了避免不穩定的測試,我們需要給測試一個穩定,干凈的環境.
測試方法中的代碼組織成三個部分來明確這些階段:
- 安排、Arrange 安排,它是定義所有變量和模型的部分。
- 行動、Action 行為:是觸發被測方法并返回結果的部分。
- 斷言` Assert 斷言:這是評估預期結果的部分。
記住AAA 是單元測試的重要部分
XCTest的使用
func test_methodOne() {
let sut = MyClass()
sut.methodOne()
// Normally, assert something
}
名稱sut代表被測系統,通常縮寫為SUT。這是“我們正在測試的東西”的常用術語。與這個簡單的例子不同,測試通常有很多對象在起作用。使用像sut這樣一致的名稱可以清楚地表明測試將作用于哪個對象。它還可以更輕松地重用測試代碼片段。
XCTest 測試機制
- XCTest 在測試時候,搜索從XCTestCase繼承的所有類。
- 對于這樣的類,它會找到每個測試方法。這些方法的名稱以test開頭,沒有參數,也沒有返回值。如
func test_methodOne()
- 對于每個這樣的測試方法,它都會創建一個類的實例。使用 Objective-C runtime,它會記住該實例將運行哪個測試方法。
- XCTest 將子類的實例收集到測試套件中。
- 當它完成所有測試用例的創建后,XCTest 才會開始運行它們。
class MyClassTests: XCTestCase {
private let sut = MyClass()
func test_methodOne() {
sut.methodOne()
// Normally, assert something
}
func test_methodTwo() {
sut.methodTwo()
// Normally, assert something
}
}
舉例
MyClassTests
有兩個測試方法 test_methodOne
,test_methodTwo
。
那么,在XCTest
運行時, 會找到MyClassTests
。它搜索以test
開頭的方法名稱,并找到兩個。所以它創建了MyClassTests
的兩個實例:一個實例運行test_methodOne
,另一個運行test_methodTwo
。
使用setUp()
,tearDown()
優化
XCTestCase定義了兩個方法,setUp和tearDown,這兩個方法設計為在子類中被覆蓋。
XCTest 中的測試運行程序保證每個測試用例的順序如下:
- 呼叫的setUp(創建對象)。
- 調用測試方法。
- 調用tearDown(銷毀)。
class MyClassTests: XCTestCase {
private var sut: MyClass!
override func setUp() {
super.setUp()
sut = MyClass()
}
override func tearDown() {
sut = nil
super.tearDown()
}
func test_methodOne() {
sut.methodOne()
// Normally, assert something
}
func test_methodTwo() {
sut.methodTwo()
// Normally, assert something
}
}
注意: 初始化存儲屬性的測試類中。要將這些屬性從let
更改為var
。并且添加!
Tips 檢測測試Log
通過如圖所示,我們可以快速找到test的失敗消息
如何寫好項目里的測試?
代碼測試覆蓋率
在 Xcode 菜單中,選擇Product
? Scheme
? Edit Scheme
...或按 ?
- <
。
現在我們就設置好了測試覆蓋率
?
+ U
試一下
并且 Xcode 菜單中選擇Editor ? Code Coverage。
紅色區域的數字代表我們這段代碼測試了幾次,在紅色條紋區域。將鼠標光標懸停在該區域,您會看到情況發生了變化,如下所示:
綠色部分顯示了我們接觸過的代碼。帶有else
的那一行部分是綠色的,部分是紅色的。這為我們提供了一種查看行內代碼覆蓋率的方法。
為現有代碼添加測試
舉例
class CoveredClass {
static func max(_ x: Int, _ y: Int) -> Int {
if x < y {
return y
} else {
return x
}
}
}
如果代碼正在使用中,我們就不需要從需求逆向工作。相反,我們可以編寫有效地使用遺留代碼稱為特性測試的內容。這些是捕獲代碼實際行為的測試。
要編寫特性測試,請執行以下操作:
- 從測試中調用代碼,產生某種結果。
- 編寫一個斷言,將結果與您知道不匹配的值進行比較。
- 運行測試。失敗消息將告訴您實際結果。
- 調整斷言,使其預期實際結果。
- 重新運行測試以查看它是否通過。
1,2部分
func test_max_with1And2_shouldReturnSomething() {
let result = CoveredClass.max(1, 2)
XCTAssertEqual(result, -123)
}
3.運行測試。這給了我們一條失敗消息
4.我們從失敗消息中復制實際值2并粘貼到斷言中
func test_max_with1And2_shouldReturn2() {
let result = CoveredClass.max(1, 2)
XCTAssertEqual(result, 2)
}
5.運行
我們本次的測試就完成了!!! 通過~
當然,如果你想要更好的測試覆蓋率,讓我們添加一個測試來覆蓋后半部分。
條件是if x < y
func test_max_with3And2_shouldReturn3() {
let result = CoveredClass.max(3, 2)
XCTAssertEqual(result, 3)
}
這應該給我們 100%
的覆蓋率。但是實際上,會由于有個}
,很難做到100%
覆蓋率
多寫幾個,也是可以完成100% 覆蓋率的,但是,我們不應該成為測試覆蓋率的奴隸.
Tips
一個好的測試名稱包含三個部分:
- 測試的內容是什么。這通常是一個函數名。
- 測試條件。有什么不同的輸入?
- 預期的結果。