IOS UI 测试 - 如何编写打开相机和文件选择器的测试用例

IOS UI Testing - How to write test cases for opening camera and file chooser

我正在使用 XCTest 框架为 IOS 编写测试用例。我想 select 一张照片用于设置用户配置文件,这可以通过两种方式完成: 1. 使用相机拍摄图像。 2. 从文件选择器中选择图像。

我遇到以下问题: 1.我在模拟器上运行这个测试。那么,如何访问相机? 2. 我可以通过单击 "Choose on Photo album" 打开页面文件选择器页面。但是,无法检测到点击页面文件选择器页面。

我的代码如下:

app.buttons["Choose from Photo Album"].tap()
app.tables.firstMatch.cells.element(boundBy: 0).tap()

第 2 行给我错误: NSInternalInconsistencyException 无效参数不满足

未找到上述案例的单个文档。

我能够使用文件选择器解决第二个用例 i.e.Setting 图像的问题。这是我的代码。

app.tables.cells.element(boundBy: 1).tap()
app.collectionViews["PhotosGridView"].cells["Photo, Landscape, March 13, 2011, 5:47 AM"].tap()
app.buttons["Choose"].tap()
app.buttons["Confirm"].tap()

第 1 行选择两个选项中的一个,即 "Moments" 和 "Camera Roll" 第 2 行从列表中选择 1 个图像 第3、4行确认并设置图像。

但我想知道它是否适用于不同的模拟器,因为图像的日期和时间在该模拟器上可能不同。

我对@deepak-terse的回答有补充:

您可以使用索引代替字符串文件名,因此您不依赖于模拟器。

func selectFromCameraRoll(_ app: XCUIApplication, index: Int = 1) {
    app.tables.cells.element(boundBy: 1).tap()
    app.collectionViews.cells.element(boundBy: index).tap()
}

您甚至可以使用 arc4random_uniform()

随机化您选择的照片

更新

万一有人发现这个有用,这里是我在真实设备上测试时使用的 UI 测试中相机的辅助函数:

 func addPhotoCamera(_ app: XCUIApplication) {
   let pleaseSelectSheet = app.sheets.element

   // Take Picture button, it is first:
   pleaseSelectSheet.buttons.element(boundBy: 0).tap()

   // this monstrosity finds Capture button
   let element = app
       .children(matching: .window).element(boundBy: 0)
       .children(matching: .other).element
       .children(matching: .other).element
       .children(matching: .other).element
       .children(matching: .other).element

   let photoCapture = element.children(matching: .other).element
       .children(matching: .other).element(boundBy: 1)
       .children(matching: .other).element

   photoCapture.tap()

   // I have slow computer, so I need this so test does not fail
   sleep(5)

   app.buttons["Use Photo"].tap()
 }

更新 2

在设备上,上面的相机胶卷不起作用,因为通常有很多照片,所以先点击不在屏幕上的照片无济于事。我最终使用了以下代码片段:

let photoCells = app.collectionViews.cells
if Platform.isSimulator {
    photoCells.element(boundBy: index).tap()
} else {
    photoCells.allElementsBoundByIndex.last!.firstMatch.tap()
}

其中 Platform.isSimulator 部分取自 :

import Foundation

struct Platform {
   static var isSimulator: Bool {
      return TARGET_OS_SIMULATOR != 0
   }
}

和整段代码一起:

struct Platform {
    static var isSimulator: Bool {
        return TARGET_OS_SIMULATOR != 0
    }
}


extension XCUIElement {
    func tapIfExists() {
        if exists {
            tap()
        }
    }
}

// MARK: - Helper functions
extension XCTestCase {
  func addPhotoCamera(_ app: XCUIApplication) {
    let pleaseSelectSheet = app.sheets.element

    //        ["Take Picture"].tap()
    pleaseSelectSheet.buttons.element(boundBy: 0).tap()

    // use coordinates and tap on Take picture button
    let element = app
        .children(matching: .window).element(boundBy: 0)
        .children(matching: .other).element
        .children(matching: .other).element
        .children(matching: .other).element
        .children(matching: .other).element

    let photoCapture = element.children(matching: .other).element
        .children(matching: .other).element(boundBy: 1)
        .children(matching: .other).element

    photoCapture.tap()

    sleep(5)

    app.buttons["Use Photo"].tap()
  }

  func addPhotoLibrary(_ app: XCUIApplication, index: Int = 0) {
    let pleaseSelectSheet = app.sheets["Add Photo"]

    pleaseSelectSheet.buttons.element(boundBy: 1).tap()

    sleep(10)

    // Camera Roll
    app.tables.cells.element(boundBy: 1).tap()

    sleep(2)

    let photoCells = app.collectionViews.cells
    if Platform.isSimulator {
        photoCells.element(boundBy: index).tap()
    } else {
        photoCells.allElementsBoundByIndex.last!.firstMatch.tap()
    }

    sleep(2)

    app.buttons["Choose"].tapIfExists()
  }
}