HireVM 工程紀錄

在雲端 Mac 建立可重複的 StoreKit 內購回歸測試

在雲端 Mac 建立可重複的 StoreKit 內購回歸測試

內購模組最難重現的故障,通常不是「按鈕無法點擊」,而是交易狀態洩漏到其他測試案例:第一個測試完成購買後,第二個測試原本應驗證未購買介面,卻直接讀到已解鎖狀態。在雲端 Mac 長期執行迴歸測試時,模擬器、StoreKit 工作階段與應用程式持久化資料必須一併隔離,否則單獨執行測試會通過、整套執行卻失敗,將成為常態。

先將測試邊界分成三層

第一層是純業務狀態機。輸入「未購買、處理中、已購買、已退款、已過期」,檢查功能權限與介面狀態,不啟動真實交易。第二層使用 StoreKit Testing,驗證商品查詢、購買回呼、交易監聽與訂閱變更。第三層才是發佈前的受控驗收,用於核對伺服器端通知與正式商品設定。

這樣拆分可讓大多數迴歸測試在本機完成,也能明確判斷失敗源自業務邏輯、用戶端交易適配,還是外部設定。不要讓每個介面測試都從點擊購買按鈕開始;狀態機單元測試應占多數,StoreKit 整合測試只需涵蓋關鍵路徑。

StoreKit Testing 提供的是可控制的交易環境,而不是正式交易流程的替代品。測試通過代表用戶端能在指定事件下正確運作,不代表外部設定已完成驗收。

建立可供審查的商品目錄

在專案中建立 StoreKit/Local.storekit,商品識別碼必須與程式碼使用的識別碼一致。建議集中定義識別碼,避免散落在檢視與測試中:

enum ProductID {
    static let proMonthly = "com.example.app.pro.monthly"
    static let proYearly = "com.example.app.pro.yearly"
}

設定檔應納入版本控制,而測試價格、顯示名稱及訂閱週期的修改也必須經過程式碼審查。團隊常見的問題是複製舊設定後只變更顯示名稱,卻沒有修改商品識別碼,導致商品查詢傳回空陣列。

為測試建立獨立 Scheme,例如 StoreKitRegression,並在 Scheme 的 Run 與 Test 選項中選擇同一份 .storekit 檔案。不要依賴開發者的個人 Scheme;只有共享 Scheme 才能由 xcodebuild 找到。

檢查項目 預期結果
商品識別碼 設定檔、程式碼與斷言完全一致
Scheme 標記為 Shared 並提交至儲存庫
訂閱群組 同一群組內的升級、降級關係清楚
本地化 測試斷言不依賴容易變動的顯示文案

每個測試案例都從乾淨的工作階段開始

使用 StoreKitTest 建立測試工作階段,並在 setUp 中關閉系統對話框、清除交易。測試方法結束後,還要清除應用程式自行儲存的權益快取。

import XCTest
import StoreKitTest

final class PurchaseRegressionTests: XCTestCase {
    private var session: SKTestSession!

    override func setUpWithError() throws {
        session = try SKTestSession(
            configurationFileNamed: "Local.storekit"
        )
        session.disableDialogs = true
        session.clearTransactions()
        UserDefaults.standard.removeObject(forKey: "cachedEntitlements")
    }

    func testMonthlyPurchaseUnlocksPro() async throws {
        try await session.buyProduct(
            identifier: ProductID.proMonthly
        )
        let unlocked = await EntitlementStore.shared.refresh()
        XCTAssertTrue(unlocked)
    }
}

交易監聽器必須在發起購買前啟動。否則購買很快完成時,監聽工作可能錯過更新,導致測試偶發逾時。退款、撤銷與訂閱到期也應由工作階段主動觸發,再針對權益重新整理後的結果進行斷言,而不是只檢查購買 API 是否傳回成功。

不要用固定等待掩蓋競爭條件

Task.sleep 只能延後失敗,無法證明狀態已完成更新。更穩妥的做法是讓權益儲存庫公開可觀察狀態,由測試等待明確條件,並設定較短的逾時時間。逾時日誌至少應記錄商品識別碼、目前權益、未完成交易數量與測試名稱,但不得輸出權杖或完整憑證。

使用命令列固定執行入口

先確認雲端 Mac 已安裝的模擬器裝置,再讓 CI 使用固定名稱。若映像檔中的裝置名稱不同,應調整流水線參數,而不是讓指令碼在未提示的情況下任意選擇裝置。

xcrun simctl list devices available
xcodebuild test \
  -workspace Example.xcworkspace \
  -scheme StoreKitRegression \
  -destination 'platform=iOS Simulator,name=iPhone 16 Pro' \
  -resultBundlePath Artifacts/StoreKitTests.xcresult \
  CODE_SIGNING_ALLOWED=NO

只執行模擬器測試時關閉簽章,可減少與交易邏輯無關的失敗。結果目錄必須在執行前清空,或依工作編號分別建立目錄,否則既有的 xcresult 會讓命令直接報錯。

StoreKit 整合測試預設採序列執行會更穩定。需要平行執行時,每個執行單元都應擁有獨立的模擬器、Derived Data 與結果目錄。多個處理程序同時操作同一台模擬器,會讓交易佇列與應用程式資料互相污染。

將失敗設計成可供定位的證據

至少涵蓋首次購買、使用者取消、待處理、退款、訂閱續期與訂閱到期六條路徑。每條路徑都應從三個層面進行斷言:StoreKit 傳回狀態、權益儲存庫狀態與最終介面狀態。只斷言按鈕文字是否變更,會漏掉背景權益未更新的問題。

提交前可依照以下順序檢查:

  1. .storekit 檔案與共享 Scheme 已納入版本控制。
  2. 每個測試案例開始前,均已清除交易與本機權益快取。
  3. 交易監聽器早於購買動作啟動。
  4. 測試不依賴固定等待或執行順序。
  5. 每個平行工作都使用獨立的模擬器與產出目錄。
  6. 發生失敗時保留 xcresult、測試日誌與介面附件。
  7. 正式發佈前另行核對伺服器端通知與商品設定。

在 HireVM 執行這類工作時,請先在控制台確認目前可選的設定,再依測試分片數量規劃模擬器。內購測試通常更容易受到狀態隔離影響,而非單次編譯速度;先確保一條序列流水線能夠重複通過,再增加並行處理,疑難排解的成本會低得多。

常見問題

StoreKit Testing 能完全取代正式環境的內購驗收嗎?

不能。它適合驗證商品對應、交易狀態、介面更新及錯誤分支;正式發布前仍須另行核對伺服器通知、實際商品設定與完整交易流程。

為什麼內購測試單獨執行成功,放入完整套件後卻失敗?

通常是前一個案例留下交易、續期狀態或本機資料。每個案例開始前都應清除測試工作階段與應用程式狀態,並避免共用同一模擬器。

StoreKit 測試可以平行執行嗎?

可以,但每個執行單元必須使用獨立模擬器及結果目錄。多個程序共用同一模擬器或 StoreKit 工作階段會降低可重現性。

專用 Apple Silicon 實體節點

將下一項遠端 Mac 任務部署到專用節點執行

依任務規模選擇兩種在售配置、五個節點,以及日租、週租、月租和季租週期。下單前即可完整核對配置與 USD 金額。

選擇配置並下單