了解 2023 年 Google I/O 大会上介绍的 Firebase 亮点。了解详情

運行遊戲循環測試

當遊戲應用程序構建在不同的 UI 框架上時,很難自動化遊戲測試。 Game Loop 測試允許您將本機測試與測試實驗室集成,並在您選擇的設備上輕鬆運行它們。本指南介紹瞭如何準備遊戲循環測試以使用 Firebase 測試實驗室運行。

關於遊戲循環測試

什麼是遊戲循環測試?

遊戲循環測試模擬真實玩家的動作,以驗證您的遊戲是否以快速且可擴展的方式為用戶提供良好的性能。循環是您對遊戲應用程序進行的完整或部分測試。您可以在模擬器或測試實驗室中的一組設備上本地運行遊戲循環測試。遊戲循環測試可用於:

  • 以最終用戶玩遊戲的方式運行您的遊戲。你可以編寫用戶輸入的腳本,讓用戶空閒,或者用 AI 代替用戶(例如,如果你在賽車遊戲中實現了 AI,你可以讓 AI 司機負責用戶的輸入) .
  • 以最高質量設置運行您的遊戲,以了解哪些設備可以支持它。
  • 運行技術測試,例如編譯多個著色器、執行它們並檢查輸出是否符合預期。

第 1 步:註冊測試實驗室的自定義 URL 方案

  1. 在 Xcode 中,選擇一個項目目標。

  2. 單擊信息選項卡,然後添加新的URL 類型

  3. URL Schemes字段中,輸入firebase-game-loop 。您還可以通過將自定義 URL 方案添加到項目的Info.plist配置文件中<dict>標記內的任意位置來註冊自定義 URL 方案:

    <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>
    

您的應用現已配置為使用測試實驗室運行測試。

第 2 步:可選擇配置您的應用程序

運行多個循環

如果您計劃在測試中運行多個循環(又名場景),則必須在啟動時指定要在應用程序中運行的循環。

在您的應用委託中,重寫application(_:open:options:)方法:

迅速

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
}

目標-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:).
  }
}

當您在測試中運行多個循環時,當前循環將作為參數傳遞給用於啟動應用程序的 URL。您還可以通過解析用於獲取自定義 URL 方案的URLComponents對象來獲取當前循環數:

迅速

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).
}

目標-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 中調用測試實驗室的自定義 URL 方案firebase-game-loop-complete來加快測試或提前結束測試。例如:

迅速

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

目標-C

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

您的遊戲循環測試終止當前循環並執行下一個循環。當沒有更多循環要運行時,測試結束。

編寫自定義測試結果

您可以配置遊戲循環測試以將自定義測試結果寫入設備的文件系統。這樣,當測試開始運行時,測試實驗室將結果文件存儲在測試設備(您必須自己創建)上的GameLoopsResults目錄中。測試結束時,測試實驗室將所有文件從GameLoopResults目錄移動到項目的存儲桶中。設置測試時請記住以下幾點:

  • 無論文件類型、大小或數量如何,都會上傳所有結果文件。

  • 直到測試中的所有循環都運行完畢後,測試實驗室才會處理您的測試結果,因此如果您的測試包含多個寫入輸出的循環,請確保將它們附加到唯一的結果文件或為每個循環創建一個結果文件。這樣,您可以避免覆蓋前一個循環的結果。

要設置測試以編寫自定義測試結果:

  1. 在您應用的Documents目錄中,創建一個名為GameLoopResults的目錄。

  2. 從您應用程序代碼的任何位置(例如,您的應用程序委託)添加以下內容:

    迅速

    /// 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.
      }
    }
    

    目標-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 協同設計

第 4 步:打包您的應用以供上傳

為您的應用程序生成一個 IPA 文件(稍後您需要找到它)。

  1. 從出現的下拉菜單中,單擊產品 > 存檔。選擇最新的存檔,然後點擊分發應用程序

  2. 在出現的窗口中,單擊Development > Next

  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 步:在本地運行測試

您可以在本地運行測試以檢查其行為,然後再使用測試實驗室運行它。要在本地測試,請在模擬器中加載您的遊戲應用程序並運行:

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運行您的測試。