iOS 穩定性:編寫會“失敗”的測試

Python實戰社羣

Java實戰社羣

長按識別下方二維碼,按需求添加

掃碼關注添加客服

進Python社羣▲

掃碼關注添加客服

進Java社羣

作者:NathanSun, iOS開發者,目前就職於字節跳動音樂團隊

Sessions: https://developer.apple.com/videos/play/wwdc2020/10091/

這個 session 的標題很有意思,編寫會“失敗”的測試。一般情況下,工程師都希望寫完測試代碼,一路“綠燈”全部通過,然後竊喜於自己寫的代碼多麼牛逼。但是真正好的測試代碼反而是能夠抓住潛在bug的測試代碼,也就是會讓測試“失敗”的代碼。請注意這個session的內容主要是講UI測試,但是同樣適用於單元測試。

測試用例可以是在本地 Xcode 跑,也可以在 CI 機器上跑。如果在 CI 上跑的話,測試結果會在 result bundle 裏面,然後我們可以根據 test bundle 打印的信息分析測試結果。好的測試代碼,應該能幫助快讀定位問題,而且更加健壯,這個 session 會告訴大家如何做到以上這兩點。我們知道,測試一般分爲三個步驟,set up, test, tear down。其中第二步 test 又可以分爲兩個子步驟,行爲(actions)和斷言(assertions),這個 session 就是以此爲框架具體闡述如何編寫好的測試代碼。

先談第一步 Set up

Xcode 11.4 增加了一個新的 API setUpWithError() throws。建議之前用 setUp() 的同學都遷移到這個新的 API,如果 setUp 過程發生錯誤,這個 API 可以拋出異常並提前終止測試。

在這個方法裏一般需要重置 app 狀態,比如上次測試權限打開了應用權限,這裏可以 reset 應用權限。一般建議設置 continueAfterFailure = false,  這樣在失敗後立即停止繼續測試,確保遇到第一個錯誤後立即停止,避免多個錯誤混在一起對排查問題造成困難。最後這個方法需要運行 app.launch() 啓動 app,啓動 app 之前也可以添加啓動參數或者環境變量,例如 app.launchArguments.append('"-recipes-tests")。加啓動參數或者環境變量的主要目的是加速你的測試過程,比如你需要跳過兩步驗證,又或者你的 app 有四個 tab,而你需要直接跳到第四個 tab 進行相關場景測試。這樣可以快速到達你的測試場景,也可以避免在其他前置場景就已經測試失敗影響你的測試。

第二步 Test

測試無非就是觸發某個行爲,並通過斷言判斷這個行爲帶來預期的結果。

行爲(Actions)

測試方法的命名很重要,一般你要想好你的測試目標是什麼,然後就用這個目的來命名。比如你是要測試一個食譜 APP  裏面的 Ingredients 列表的正確性,那麼你可以將測試方法命名爲 testIngredientsListAccuracy。下面是這個測試方法的具體內容,即測試 APP 裏面的一個奶昔列表,選中一個品類,就可以看到這個品類對應的原料詳情頁。

在實際編寫測試行爲代碼的過程中,總結下來有下面的小技巧:

  1. 同一個 UI 的名字經常變怎麼辦?可以用enum來解決,把字符串轉成 enum 的形式來表達,這樣如果 Label 的名字變了,只需要把 enum 對應的 string 值直接修改即可。

  2. 把多個測試中出現的重複代碼包裝成 helper function。

  3. 用面向對象的方法去組織測試代碼,把測試代碼寫成易讀的形式,比如 app.smoothieList().select(smoothie: .berryblue) 一眼就能看出是想要做的事情是從列表中點擊一個元素。

  4. 隨着測試代碼越來越龐大,可以把一些測試代碼用產品代碼一樣的方式管理起來,比如包裝成 framework 或者是 Swift package,這樣可以在多個項目之間共享測試代碼。

斷言(Aassertions)

斷言就是判斷結果是否符合預期,使用斷言時需要注意以下幾點。

  1. 不要忽視 XCTAssert 裏面的可選變量 message 的作用。測試更多的時候是跑在 CI 機器上,所以有越多的上下文信息肯定對定位問題越有幫助。比如 XCTAsssertEqual(count, expectedCount),這是一個不太好的寫法,完全缺失了上下文信息,比較好的寫法是如下圖所示,把上下文信息補充在 message 裏面。很多項目會用自動化工具去收集彙總這些測試結果,所以一般建議不要在上下文信息裏面裏面包含特定文件名路徑、時間戳等,而是隻留下一些通用信息,這樣可以確保同樣的錯誤可以聚合在一起。

  1. 根據實際情況選用最恰當的 Assert 語句,能起到事半功倍的作用。可以考慮使用在 Xcode 12.0 中,新加入的 XCTIssue。關於這個API的詳細信息,請參考這個 session[1]

  1. 處理異步的測試任務,比如點擊一個button獲取網絡數據,等待網絡數據返回並檢查結果。有幾種做法:第一種就是 sleep 一段時間盲等待。另一種辦法是用 waitForExistence(timeout: 5),這樣會用輪詢的辦法去檢查結果,會更加高效。推薦使用第二種辦法。

  2. 解 optional 最好用 XCTUnwrap。舉個例子,favorites 是一個 optional,可以用 try XCTUnwrap(favorites, "favorites is nil, so there is nothing to count"),這樣如果 favorites 是一個 nil 值,會拋出異常,測試程序不會 crash 而且 tear down 能繼續執行。

  3. 如果是使用公共庫中的測試代碼,可以從公共庫中拋出異常,並打印出足夠多的信息,方便檢查問題。

  4. 多使用 XCTContext.runActivity(named:, block:) 描述測試的上下文信息,也可以在 context 裏面加上 XCTAttachment,比如文件、數據、圖片信息等。

  5. 善用 XCTSkipUnless, XCTSkipIf 跳過一些測試,  這是在 Xcode 11.4 開始提供的 API。這樣,測試結果報告中會標記提醒哪些測試過程是暫時跳過的,將來你就不會忘記還需要補充這些功能。一般用在以下場景中。

  • 跳過不相關的平臺或者系統版本

  • 跳過還沒有來得及實現的新測試代碼

  • 暫時無法修復的測試 bug

最後來看一下 tear down

關於 tear down,有三點建議。

  1. 使用 XCode 11.4 提供的新 API,func tearDownWithError() throws。

  2. 在 tear down 方法里加上一些額外的日誌,也可以提供一些簡單的失敗結果分析。

  3. 可以重置 setup 過程中的一些設置,避免對下次測試造成干擾。

參考資料

[1]

session: https://developer.apple.com/videos/play/wwdc2020/10687/

程序員專欄 掃碼關注填加客服 長按識別下方二維碼進羣

近期精彩內容推薦:  

 又一個程序員,被抓捕!(真實事件)

 程序員有個可愛女朋友是種什麼體驗?

 “12306”的架構到底有多牛逼?

 csv文件讀寫亂碼問題的一個簡單解決方法


在看點這裏好文分享給更多人↓↓

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章