執行遊戲迴圈測試

如果遊戲應用程式是以不同的 UI 架構建構而成,要自動執行遊戲測試並不容易。透過遊戲迴圈測試,您可以將原生測試與 Test Lab 整合,並在所選裝置上輕鬆執行測試。本指南說明如何準備遊戲迴圈測試,以便使用 Firebase Test Lab 執行。

關於遊戲迴圈測試

什麼是遊戲迴圈測試?

遊戲迴圈測試會模擬真實玩家的動作,以快速且可擴充的方式驗證遊戲能否為使用者提供良好體驗。迴圈是指遊戲應用程式上完整或部分執行的測試。您可以在本機模擬工具或 Test Lab 的一組裝置上執行遊戲迴圈測試。遊戲迴圈測試可用於:

  • 讓使用者透過遊戲執行整個遊戲。您可以編寫使用者輸入內容的指令碼、讓使用者處於閒置狀態,或是用 AI 取代使用者。舉例來說,如果您在賽車遊戲中導入 AI 技術,就可以讓 AI 驅動程式主導使用者的輸入內容。
  • 以最高品質設定執行遊戲,找出支援這款遊戲的裝置。
  • 執行技術測試 (例如編譯及執行多個著色器),然後檢查輸出內容是否符合預期。

步驟 1:註冊 Test Lab 的自訂網址通訊協定

  1. 在 Xcode 中選取專案目標。

  2. 按一下「資訊」分頁標籤,然後新增「網址類型」

  3. 在「URL Schemes」(網址配置) 欄位中輸入 firebase-game-loop。您還可以將自訂網址通訊協定新增至專案的 Info.plist 設定檔,於 <dict> 標記內的任意位置加入:

    <key>CFBundleURLTypes</key>
     <array>
         <dict>
             <key>CFBundleURLName</key>
             <string></string>
             <key>CFBundleTypeRole</key>
             <string>Editor</string>
             <key>CFBundleURLSchemes</key>
             <array>
                 <string>firebase-game-loop</string>
             </array>
         </dict>
     </array>
    

應用程式現已設為使用 Test Lab 進行測試。

步驟 2:視需要設定應用程式

執行多個迴圈

如果您打算在測試中執行多個迴圈 (即情境),則必須在啟動時指定要在應用程式中執行的迴圈。

在應用程式委派項目中,覆寫 application(_:open:options:) 方法:

Swift

func application(_app: UIApplication,
                 open url: URL
                 options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    let components = URLComponents(url: url, resolvingAgainstBaseURL: true)!
    if components.scheme == "firebase-game-loop" {
        // ...Enter Game Loop Test logic to override application(_:open:options:).
    }
    return true
}

Objective-C

- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary &lt;UIApplicationOpenURLOptionsKey, id&gt; *)options {
  if ([url.scheme isEqualToString:(@"firebase-game-loop")]) {
      // ...Enter Game Loop Test logic to override application(_:open:options:).
  }
}

在測試中執行多個迴圈時,系統會將目前的迴圈,以參數的形式傳遞到用於啟動應用程式的網址。您也可以剖析用於擷取自訂網址配置的 URLComponents 物件,以取得目前的循環編號:

Swift

if components.scheme == "firebase-game-loop" {
    // Iterate over all parameters and find the one with the key "scenario".
    let scenarioNum = Int(components.queryItems!.first(where: { $0.name == "scenario" })!.value!)!
    // ...Write logic specific to the current loop (scenarioNum).
}

Objective-C

if ([url.scheme isEqualToString:(@"firebase-game-loop")]) {
    // Launch the app as part of a game loop.
    NSURLComponents *components = [NSURLComponents componentsWithURL:url
                                             resolvingAgainstBaseURL:YES];
    for (NSURLQueryItem *item in [components queryItems]) {
        if ([item.name isEqualToString:@"scenario"]) {
            NSInteger scenarioNum = [item.value integerValue];
            // ...Write logic specific to the current loop (scenarioNum).
        }
    }
}

提早結束測試

根據預設,即使已執行所有迴圈,遊戲迴圈測試仍會持續執行,直到逾時時間達到五分鐘為止。逾時期間,測試就會結束,並取消任何待處理的迴圈。如要加快測試或提早結束測試,您可以在應用程式的 AppDelegate 中呼叫 Test Lab 的自訂網址配置 firebase-game-loop-complete。例如:

Swift

/// End the loop by calling our custom url scheme.
func finishLoop() {
    let url = URL(string: "firebase-game-loop-complete://")!
    UIApplication.shared.open(url)
}

Objective-C

- (void)finishLoop {
  UIApplication *app = [UIApplication sharedApplication];
  [app openURL:[NSURL URLWithString:@"firebase-game-loop-complete://"]
      options:@{}
completionHandler:^(BOOL success) {}];
}

遊戲迴圈測試會終止目前的迴圈,並執行下一個迴圈。如果沒有其他可執行的迴圈,測試就會結束。

寫入自訂測試結果

您可以設定遊戲迴圈測試,將自訂測試結果寫入裝置的檔案系統。這樣一來,測試開始執行時,Test Lab 會將結果檔案儲存在測試裝置的 GameLoopsResults 目錄中 (您必須自行建立)。測試結束後,Test Lab 會將所有檔案從 GameLoopResults 目錄移至專案的值區。設定測試時,請注意下列事項:

  • 無論檔案類型、大小或數量為何,系統都會上傳所有結果檔案。

  • 在測試中的所有迴圈都執行完畢之前,Test Lab 不會處理測試結果,因此如果測試中包含會寫入輸出內容的多個迴圈,請務必將這些迴圈附加到不重複的結果檔案中,或是為每個迴圈建立結果檔案。如此一來,就可以避免覆寫先前迴圈的結果。

如何設定測試以寫入自訂測試結果:

  1. 在應用程式的 Documents 目錄中,建立名為 GameLoopResults 的目錄。

  2. 在應用程式程式碼的任一處 (例如應用程式委派) 新增以下內容:

    Swift

    /// Write to a results file.
    func writeResults() {
      let text = "Greetings from game loops!"
      let fileName = "results.txt"
      let fileManager = FileManager.default
      do {
    
      let docs = try fileManager.url(for: .documentDirectory,
                                     in: .userDomainMask,
                                     appropriateFor: nil,
                                     create: true)
      let resultsDir = docs.appendingPathComponent("GameLoopResults")
      try fileManager.createDirectory(
          at: resultsDir,
          withIntermediateDirectories: true,
          attributes: nil)
      let fileURL = resultsDir.appendingPathComponent(fileName)
      try text.write(to: fileURL, atomically: false, encoding: .utf8)
      } catch {
        // ...Handle error writing to file.
      }
    }
    

    Objective-C

    /// Write to a results file.
    - (void)writeResults:(NSString *)message {
        // Locate and create the results directory (if it doesn't exist already).
        NSFileManager *manager = [NSFileManager defaultManager];
        NSURL* url = [[manager URLsForDirectory:NSDocumentDirectory
                                      inDomains:NSUserDomainMask] lastObject];
        NSURL* resultsDir = [url URLByAppendingPathComponent:@"GameLoopResults"
                                                 isDirectory:YES];
        [manager createDirectoryAtURL:resultsDir
          withIntermediateDirectories:NO
                           attributes:nil
                                error:nil];
    
        // Write the result message to a text file.
        NSURL* resultFile = [resultsDir URLByAppendingPathComponent:@"result.txt"];
        if ([manager fileExistsAtPath:[resultFile path]]) {
            // Append to the existing file
            NSFileHandle *handle = [NSFileHandle fileHandleForWritingToURL:resultFile
                                                                     error:nil];
            [handle seekToEndOfFile];
            [handle writeData:[message dataUsingEncoding:NSUTF8StringEncoding]];
            [handle closeFile];
        } else {
            // Create and write to the file.
            [message writeToURL:resultFile
                     atomically:NO
                       encoding:NSUTF8StringEncoding error:nil];
        }
    }
    

步驟 3:簽署應用程式

  1. 確認應用程式中的所有構件皆已簽署。例如,您可以透過 Xcode 指定簽署設定,例如佈建設定檔與身分。詳情請參閱 Apple Codesigning

步驟 4:封裝應用程式以供上傳

為應用程式產生 IPA 檔案 (稍後會用到)。

  1. 在顯示的下拉式選單中,依序點選「產品」>「封存」。 選取最新的封存檔案,然後點選「發布應用程式」

  2. 在隨即顯示的視窗中,依序按一下「開發」>「下一步」

  3. 選用:如要加快建構速度,請取消選取「Rebuild from Bitcode」選項,然後點選「Next」。執行測試時不必使用精簡或重建應用程式即可進行測試,因此您可以放心停用這個選項。

  4. 按一下「Export」,然後輸入要下載應用程式 IPA 檔案的目錄。

步驟 5:驗證應用程式簽章

  1. 將 .ipa 檔案解壓縮,然後執行 codesign --verify --deep --verbose /path/to/MyApp.app,其中「MyApp」是解壓縮資料夾中的應用程式名稱 (依專案而異),即可驗證應用程式簽名。預期的輸出內容為 MyApp.app: valid on disk

步驟 6:在本機執行測試

您可以在本機執行測試,先檢查其行為,再使用 Test Lab 執行測試。如要在本機上測試,請在模擬器中載入遊戲應用程式,然後執行下列指令:

xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://
  • 您可以執行 instruments -s devices 指令,找出模擬器的 UDID。

  • 如果只有一個模擬工具正在執行,請輸入特殊字串 "booted" 取代 SIMULATOR_UDID

如果測試包含多個迴圈,則可將迴圈編號傳遞至 scenario 標記,藉此指定要執行的迴圈。請注意,在本機執行測試時,一次只能執行一個迴圈。舉例來說,如果您想要執行 1、2 和 5 迴圈,則必須針對每個迴圈分別執行指令:

xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=1
xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=2
xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=5

後續步驟

請使用 Firebase 控制台gcloud CLI 執行測試。