React-Native 與 XCUIUnitTest 之 iOS 自動化 UI 測試教學 — 使用 XCode 與 Swift

利用 XCode 的 XCUIUnitTest 來進行 UI 自動測試

Seachaos
tree.rocks

--

前言

任何軟體開發完成以後,最重要的就是確保可以執行正確
如果是純 API Server 這種與 UI 無關的其實測試相對容易,就是 integration / unit test

但是牽扯到 UI 的話,測試就相對麻煩,React-Native 有提供 jest 相關的測試,但這部分比較像是做 snapshots 去確保程式改動過程中不會改變 UI ( 這部分如果改天有空再另開文章介紹 )

本教學的主軸就是利用 XCode 的 XCUIUnitTest 來進行 UI 的自動測試
我們不做 react-native 基本安裝與 test case 大理論介紹

1. 準備專案

如果您已經有現成專案,可以跳過此步驟

輸入

react-native init myuitest

建立一個新的專案,然後修改內容,準備以下 Component 作為測試使用

我們將放入到 App.js 中,隨便找個地方放置元件確定有被 render 就好,例如

<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>Step One</Text>
<MyViewWithButton /> {/* <- 放在 StepOne 之下 */}
<PressCounter /> {/* <- 放在 StepOne 之下 */}
<Text style={styles.sectionDescription}>
...
</View>

2. 建立測試

從專案中找到 ios/*.xcworkspace 檔案,如果是以上範例的話就是 ios/myuitest.xcworkspace

開啟 xcode 專案

React-Native 已經幫我們將基本的 XCode UnitTest Project 建立好了
只需到要 XCode 中的 Test Navigator 建立 UI Test Target( 如附圖 )

本教學使用 Swift 為主,當然也可以使用 Objective-C ( 考量日後 Apple 支援度,建議還是 Swift 比較保險… )

另外記得 Create Bridging Header,這可以讓 Swift 與 Objective-C 互通

3. 準備攥寫測試

可以看到強大的 XCode 已經幫我們建好一些範例,在這之前我們要先確認專案可以正常開啟且運作,如下圖

React-Native App 運作正常

如果 React-Native App 執行順利,我們就可以跑一下 XCode Test 看看

題外話,如果遇到類似以下錯誤

iOS Simulator 12.4 doesn’t match myuitestUITests.xctest’s iOS Simulator 13.7 deployment target. Upgrade iPhone SE’s iOS Simulator version or lower myuitestUITests.xctest’s deployment target.

這是因為預設專案需要至少 iOS 13.7 以上,兩個解決辦法 :
1. 使用 iOS 13.7 以上模擬器
2. 降低需求版本 <- 本人使用此解決方案 ( 參考下圖 )

降低測試專案的 iOS 版本需求

應該會看到 XCode 幫我們建立的基本測試成功

4. 開始施工 — XCUITest 基本介紹

我們現在來看 testExample 的個 function

let app = XCUIApplication()
app.launch()

這個是啟動我們的 React-Native App,基本上每個 test function 可以看作是一個獨立的事件,所以每次的測試都需要打開一次 App

在這之前大多數的 React-Native 幫我們建立的專案大多奇妙的使用 2 個空白作為縮排,這裡提供 XCode 縮排修改的方式,改成慣用的 4 個空白比較舒適

將 XCode 預設縮排做調整, 2 空白改成 4空白

現在建立一個 function ( swift code ),以點擊自動測試點擊 UI 上的按鈕為例

Swift 的 XCUITest Code
func testButtonTap() throws {
let app = XCUIApplication()
app.launch()

var ele:XCUIElement? = nil
ele = app.otherElements["test_id_a"]
ele?.tap()

ele = app.otherElements["test_id_b"]
ele?.tap()

ele = app.otherElements["MyButton"]
ele?.tap()

ele = app.otherElements["You Got"]
ele?.tap()

ele = app.otherElements["label_press"]
ele?.tap()

ele = app.otherElements["label_press"]
ele?.tap()
sleep(10)
}

如果執行可以觀察到 UI 上的按鈕確實被點擊了
這也是我們想到達到的自動 UI 測試 ( 每被點擊一次計數器就會 + 1 )

XCUITest 測試點擊按鈕

並且測試通過了

測試通過

解說

app.otherElements 就是在 App 中尋找元素
這個元素一般來說可以是 :

  1. React-Native App 的 Text component ( 內文 )
  2. Component 的 TestID ( 如下附圖 )
  3. accessibilityLabel, 例如我們 PressCounter Component 的內文是會改變的,就可以利用這個技巧來讓 XCUIElementQuery 找到元素
TestID 讓 XCUIElementQuery 找到
accessibilityLabel 讓 XCUIElementQuery 找到

另外 Sleep 這個 function 是用來讓我們觀察 test case 的,實務上並不需要 ( 因為電腦可以高速自動完成測試 )

sleep function 可以參考此原始碼

5. 打字測試 ( TextInput 與 XCUITest )

XCUITest 在 React-Native 中不只可以點擊按鈕,還可以輸入文字
我們這邊隨便做一個 TextInput 在 App.js 中,並且指定 testID

我們使用 testID

以下是我們的打字 test function ( Swift Code )

func testTypeText() {  let app = XCUIApplication()
app.launch()
var ele:XCUIElement? = nil
ele = app.textFields["input_example"]
ele?.tap()
let deleteString = String(repeating: XCUIKeyboardKey.delete.rawValue, count: 10)
ele?.typeText(deleteString)
ele?.typeText("this is so cool") sleep(10)
}

測試成功會看到輸入框已經被修改

測試自動打字

解說

  var ele:XCUIElement? = nil
ele = app.textFields["input_example"]
ele?.tap()

這邊一樣先抓出元素, 然後文字輸入框和我們操作手機邏輯一樣,需要點擊他,所以有個 tap

接下來是刪除原本內容 ( 送出刪除字元 ),如果沒有這段的話文字會附加在後面

  let deleteString = String(repeating: XCUIKeyboardKey.delete.rawValue, count: 10)
ele?.typeText(deleteString)

再來就是打上內文

ele?.typeText("this is so cool")

檢查元件是否存在

如果要檢查是否這個元件存在畫面上,可以用以下語法

if (ele?.exists == true) {
NSLog("YES")
} else {
NSLog("NO")
}

後記

以上是簡單的 React-Native 與 XCode 的自動化 UI 測試教學
實務上依照自己的 APP 開發流程與 UI 去做出不同的 Test Case

如果有問題歡迎留言討論

更多的說明可以參考 Apple 官方

--

--