从拆分视图控制器内部测试 UITableViewCell 是否存在失败

Test for UITableViewCell existence failing from inside a split view controller

我正在测试 table 视图单元格的存在,下面的代码在 iPhone 7:

上工作得很好
let complaintCell = self.app.tables.cells.element(boundBy: 0)
XCTAssert(complaintCell.exists)
complaintCell.tap()

现在的问题是,如果我 运行 在视图控制器嵌入拆分视图控制器的 iPad 上进行相同的测试,则测试失败:

table 视图在主视图控制器上仍然可见:

所以我找不到测试失败的原因,即使 table 视图是唯一可见的。有什么提示吗?

完整代码:

func testNavigation() {
    let complaintCell = self.app.tables.cells.element(boundBy: 0)
    XCTAssert(complaintCell.exists)
    complaintCell.tap()

    XCTAssert(self.app.navigationBars["Complaint #100"].exists)
    XCTAssertFalse(self.app.navigationBars["Complaint #99"].exists)

    let editButton = self.app.buttons["editComplaint"]
    XCTAssert(editButton.exists)
    editButton.tap()

    XCTAssert(self.app.navigationBars["Complaint #100"].exists)
    XCTAssertFalse(self.app.navigationBars["Complaint #99"].exists)

    let saveButton = self.app.buttons["Save"]
    XCTAssert(saveButton.exists)
    saveButton.tap()

    let okButton = self.app.buttons["Ok"]
    XCTAssert(okButton.exists)
    okButton.tap()
}

更新

我能够隔离问题:如果我只是创建一个新项目,一个主细节应用程序并设置主 table 视图的可访问性标识符,然后我测试它是否存在,测试失败:

let table = self.app.tables["TableView"]
XCTAssert(table.waitForExistence(timeout: 5.0))

重现此问题的必要步骤非常简单,您只需创建一个主从应用程序,设置 table 视图的可访问性标识符,然后 运行 上面的代码.但如果你愿意,你也可以克隆这个存储库,我用它作为演示来隔离问题:https://github.com/ralzuhouri/tableViewTestDemo

我在尝试在模拟器上测试 tableview 单元是否存在时遇到了同样的问题。在模拟的 iPhone 设备上,测试会成功,而在 iPad 设备上,它会失败。

我发现问题在于,如果应用程序的当前视图没有您要测试其数据的 table 视图,那么引用 table 的 UITest 将失败。在 iPhone 上,默认情况下视图有一个后退按钮,可以将应用程序从其详细视图转换回包含 table 视图的主视图 viewcontroller。在 iPad 模拟器上没有这个后退按钮,因此它无法正确转换到 table 视图,导致整个测试失败。

func testTableCellsExist() {

    let app = XCUIApplication()
    app.launch()

    app.navigationBars["AppName.DetailView"].buttons["Root View Controller"].tap()

    let tablesQuery = app.tables["MasterTable"]

    let testCell = tablesQuery.cells.element(boundBy: 49)
    XCTAssert(tablesQuery.cells.count == 50)
    XCTAssert(testCell.exists)
}

为了使 iPad 和 iPhone 设备模拟的测试成功,我所做的就是让应用程序启动并显示 table 视图的 viewcontroller 一开始。这是通过将此代码添加到 UISplitViewController swift 文件来完成的:

class SplitViewController: UISplitViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    self.delegate = self
    self.preferredDisplayMode = .allVisible
}

}

extension SplitViewController: UISplitViewControllerDelegate {
    func splitViewController(
             _ splitViewController: UISplitViewController,
             collapseSecondary secondaryViewController: UIViewController,
             onto primaryViewController: UIViewController) -> Bool {
        // Return true to prevent UIKit from applying its default behavior
        return true
    }

}

上面代码的解释可以在这里找到:

无论如何,经过上述修改后,table视图单元格存在的断言现在应该成功,即使在 iPad 上也是如此,因为视图现在已正确设置为具有 table正在查询的视图。

如果您不希望您的应用默认以该视图启动,则必须确保您的测试代码在进行断言之前转换到 table 视图的视图。

此外,如果您按照我的示例进行操作,请务必在测试用例中删除这一行,因为在修改为UISplitViewController 代码:

app.navigationBars["AppName.DetailView"].buttons["Root View Controller"].tap()

更新(10 月 25 日):Master Detail App 项目 - 基本 TableView 存在性测试

我尝试按照您的建议创建一个基本的 Master Detail 应用程序,并尝试了测试。当我选择 iPad 设备进行模拟时,table 视图的基本测试再次失败,因为它只显示详细视图(没有 table)。我修改了我的测试,如果设备是 iPad,它将检查其方向,并根据需要设置横向,然后再检查 table 是否存在。我只修改了测试,没有修改其他任何东西,之前失败的变成了成功。我还在 MasterVC 的 viewDidLoad 中为 table 视图设置了可访问性标识符,但我相信无论是否设置标识符,结果都是一样的。这是测试代码:

func testExample() {
    // UI tests must launch the application that they test.
    let app = XCUIApplication()
    app.launch()

    // Use recording to get started writing UI tests.
    // Use XCTAssert and related functions to verify your tests produce the correct results.

    if UIDevice.current.userInterfaceIdiom == .pad {

        print("TESTING AN IPAD\n")
        print(XCUIDevice.shared.orientation.rawValue)

        // For some reason isPortrait returns false
        // If rawValue == 0 it also seems to indicate iPad in portrait mode
        if (XCUIDevice.shared.orientation.isPortrait || XCUIDevice.shared.orientation.rawValue == 0){
            XCUIDevice.shared.orientation = .landscapeRight
        }
        XCTAssert(app.tables["MyTable"].exists)
        //XCTAssert(app.tables["MyTable"].waitForExistence(timeout: 5.0))
    } else if UIDevice.current.userInterfaceIdiom == .phone {
        print("TESTING AN IPHONE\n")
        XCTAssert(app.tables["MyTable"].exists)
    }


    // XCTAssert(app.tables["MyTable"].exists)

}

我为 iPhone 案例添加了一个 else,并打印日志以说明正在测试哪种类型的设备。无论使用 .exists 还是 .waitForExistence,测试都应该总是成功的。但正如所指出的那样,在 table 视图需要时间加载的情况下,.waitForExistence 更好。

希望这对您有所帮助。干杯!

更新(10 月 27 日):在真实 iPad

上测试失败

OP 发现测试——在模拟 iPhone 和 iPad 设备上成功——在真实设备上失败(有关更多信息,请查看他对下面这个答案的信息性评论).由于测试在已经处于横向模式的真实 iPad 上失败(见屏幕截图),因此可以假设 XC UITest 功能在这方面被破坏了。

无论如何,我希望这些测试和结果对其他人也有帮助(他们当然教会了我一两件事)。

干杯:D

您的问题似乎与设备的方向有关,正如我在您的测试项目中尝试时所注意到的那样。我设法提出了两种解决方案,两者都可以根据代码中的注释完美地在 1 或 1a 之间进行选择。选项 1 适用于两种 iPhone,iPads 选项 1a 是 iPad 特定的或更大的 iPhone,也可以横向显示母版。

首先在您的代码中设置 tableViews 可访问性标识符,以便轻松引用正确的 tableView:

complaintTableView.accessibilityIdentifier = "ComplaintsTableView"

然后在您的测试中只需引用标识符 - 这是根据您上述问题的完整测试:

func testNavigation() {
    // 1 - Hit the back button on the collapset master view
    let masterButton = app.navigationBars.buttons.element(boundBy: 0)
    if masterButton.exists {
        masterButton.tap()
        print("BACK TAPPED")
    }
    // 1a - Or just use the line below to rotate the view to landscape which will automatically show the master view
    XCUIDevice.shared.orientation = UIDeviceOrientation.landscapeRight

    let complaintsTable = app.tables["ComplaintsTableView"]
    XCTAssertTrue(complaintsTable.exists, "Table does not exist")
    let complaintCell = complaintsTable.cells.firstMatch
    XCTAssert(complaintCell.exists)
    complaintCell.tap()

    XCTAssert(self.app.navigationBars["Complaint #100"].exists)
    XCTAssertFalse(self.app.navigationBars["Complaint #99"].exists)

    let editButton = self.app.buttons["editComplaint"]
    XCTAssert(editButton.exists)
    editButton.tap()

    XCTAssert(self.app.navigationBars["Complaint #100"].exists)
    XCTAssertFalse(self.app.navigationBars["Complaint #99"].exists)

    let saveButton = self.app.buttons["Save"]
    XCTAssert(saveButton.exists)
    saveButton.tap()

    let okButton = self.app.buttons["Ok"]
    XCTAssert(okButton.exists)
    okButton.tap()
}

您可能会使用 waitForExistence() 而不是 .exists

.exists 一调用就执行。您的 TableView 此时可能还没有准备好接受测试。

我会尝试用 waitForExistence()

替换 .exists
let complaintCell = self.app.tables.cells.element(boundBy: 0)
XCTAssert(complaintCell.waitForExistence(timeout: 10))
complaintCell.tap()

你也可以放弃这个 XCTAssert

tap() 将等待存在 3 秒,然后如果单元格不存在则产生错误消息。

缩短后的版本为:

app.tables.cells.firstMatch.tap()

我已经下载了你的演示。

虽然 iPhone 应用程序以主视图启动,但 iPad 应用程序以 SplitView 或详细视图(纵向)启动。

纵向视图应该从主视图+详细视图开始。更改 UI 测试的其他选项如下:

func testExample() {
    let table = self.app.tables["TableView"]
    if !table.waitForExistence(timeout: 1) {
        app.navigationBars.element.swipeRight()
        XCTAssert(table.waitForExistence(timeout: 2))
    }
    ...
}