The real test coverage in iOS
我們在寫 Unit Test 或者 UI Test 的時候,除了看測項有沒有通過以外,另一個很常看的指標是 coverage 覆蓋率,究竟有多少比例的 line of code 是被測過的。Coverage 比例可以在 test report 裡面看到,也可以轉換成 Cobertura 的格式放到 Jenkins 上,步驟參考阿伯寫的教學文。
coverage 的算法是有執行到的程式碼都標記起來,有執行到的程式碼行數除以所有程式碼行數就是 coverage 比例,可以算整個 app 的 coverage 也可以算某個檔案的 coverage。也可以做一些調整,像是某些檔案不要算進去之類的,詳情可以看 slather 的設定。既然是有執行到的程式碼,那一定都是 unit test 觸發的嗎?答案是否定的,Unit Test 執行的時候,會用模擬器打開 app,這個時候就會開始執行第一個畫面該做的事。
執行到的程式碼就會包括
- AppDelegate 的 didFinishLaunchingWithOptions 跟 applicationDidBecomeActive
- 第一個 ViewController 的 viewWillAppear, viewDidAppear,viewDidLoad, viewDidLayoutSubviews
還有裡面全部執行到的 code。像是 call 到的 third party framework,呼叫的 API,跟 API handler。
這延伸出的問題就是誤以為自己 coverage 很高,自以為很嚴謹。孰不知那邊只是執行到而已,並沒有測到。
在跑 Unit Test 的時候,可以去讀 ProcessInfo 裡面的 XCTestConfigurationFilePath 確認現在是不是在 Unit Test。如下圖
解法我覺得有兩種。
一種是上圖的方法,直接拆 AppDelegate,TestAppDelegate 裡面就是個空的 class,甚至連 window 都沒有,這裡使用到的技巧是拆 main.swift。
第二種就是在原先的 AppDelegate 裡面,在 DidFinishLaunching 跟 becomeActive 都檢查現在是不是在 Unit Test,是的話就 return,不執行接下來的動作。
這樣算出來的 coverage 才是真的 coverage。