iOS UnitTest 學習 (二) 了解單元測試的機制,以及如何寫好單元測試

前言

除了斷言,還有更多的測試。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_methodOnetest_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的失敗消息


截屏2021-07-07 下午8.03.37.png

如何寫好項目里的測試?

代碼測試覆蓋率

在 Xcode 菜單中,選擇Product ? Scheme ? Edit Scheme...或按 ?- <

截屏2021-07-07 下午9.13.20.png

現在我們就設置好了測試覆蓋率

?+ U 試一下

截屏2021-07-07 下午9.21.27.png

并且 Xcode 菜單中選擇Editor ? Code Coverage。

截屏2021-07-07 下午9.23.06.png

紅色區域的數字代表我們這段代碼測試了幾次,在紅色條紋區域。將鼠標光標懸停在該區域,您會看到情況發生了變化,如下所示:

截屏2021-07-07 下午9.24.42.png

綠色部分顯示了我們接觸過的代碼。帶有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)
    }
截屏2021-07-07 下午9.33.33.png

3.運行測試。這給了我們一條失敗消息

4.我們從失敗消息中復制實際值2并粘貼到斷言中

    func test_max_with1And2_shouldReturn2() {
        let result = CoveredClass.max(1, 2)

        XCTAssertEqual(result, 2)
    }

5.運行

截屏2021-07-07 下午9.37.40.png

我們本次的測試就完成了!!! 通過~

當然,如果你想要更好的測試覆蓋率,讓我們添加一個測試來覆蓋后半部分。
條件是if x < y

    func test_max_with3And2_shouldReturn3() {
        let result = CoveredClass.max(3, 2)

        XCTAssertEqual(result, 3)
    }

這應該給我們 100% 的覆蓋率。但是實際上,會由于有個},很難做到100%覆蓋率

截屏2021-07-07 下午9.45.53.png

多寫幾個,也是可以完成100% 覆蓋率的,但是,我們不應該成為測試覆蓋率的奴隸.

Tips

一個好的測試名稱包含三個部分:

  • 測試的內容是什么。這通常是一個函數名。
  • 測試條件。有什么不同的輸入?
  • 預期的結果。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容