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

时间:2019-10-20 17:16:35

标签: ios swift xctest ui-testing

我正在测试是否存在表格视图单元格,并且以下代码在iPhone 7上可以正常运行:

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

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

enter image description here

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

enter image description here

所以即使表视图是唯一可见的视图,我也无法找到测试失败的原因。有提示吗?

完整代码:

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

更新

我能够找出问题所在:如果我只是创建一个新项目,一个主明细应用程序,然后设置主表视图的可访问性标识符,然后测试其存在性,则测试将失败:

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

重现此问题所需的步骤非常简单,只需创建一个主详细信息应用程序,设置表视图的可访问性标识符,然后运行上面的代码。但是,如果您愿意,也可以克隆此存储库,我已将其用作演示以找出问题所在:https://github.com/ralzuhouri/tableViewTestDemo

4 个答案:

答案 0 :(得分:0)

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

我发现问题在于,如果应用程序的当前视图没有要测试其数据的tableview,则引用表的UITest将失败。在iPhone上,视图默认情况下具有后退按钮,该按钮会将应用程序从其详细信息视图转换回包含表视图的主视图控制器。在iPad模拟器上,此后退按钮不存在,因此无法正确过渡到表视图的视图,从而使整个测试失败。

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设备仿真都能成功通过测试,是为了使该应用程序启动并在一开始就显示tableview的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
    }

}

以上代码的说明可以在这里找到:

Open UISplitViewController to Master View rather than Detail

无论如何,在进行了上述修改之后,即使在iPad上,对于tableview单元的存在的断言现在也应该成功,因为该视图现在已正确设置为要查询tableview的视图。

如果您不希望您的应用默认情况下从此视图开始,那么您必须确保在进行断言之前,将测试代码转换为表视图的视图。

此外,如果您按照我的示例进行操作,请确保在测试用例中删除此行,因为在修改UISplitViewController代码后,不再需要导航到表视图的VC:

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

更新(10月25日):主要详细信息应用程序项目-基本的TableView存在测试

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

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外壳添加了另一个,并打印了日志以说明正在测试哪种类型的设备。无论使用.exists还是.waitForExistence,测试都应始终成功。但正如前面指出的那样,如果表视图需要花费一些时间来加载,.waitForExistence会更好。

希望这会有所帮助。干杯!

更新(10月27日):在Real iPad上测试失败

OP已经识别出该测试-在模拟的iPhone和iPad设备上成功-在真实设备上失败(有关更多信息,请在下面的答案中查看其翔实的评论)。由于在已经处于横向模式的真实iPad上测试失败(请参见屏幕截图),因此可以认为XC UITest功能在这方面已被破坏。

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

干杯:D

答案 1 :(得分:0)

您的问题似乎与设备的方向有关,正如我在测试项目中尝试该设备时所注意到的那样。我设法提出了两个解决方案,根据代码中的注释,它们都可以在1或1a之间完美选择。选项1可以在iPhone和iPad上使用。选项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()
}

答案 2 :(得分:0)

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

.exists会在调用后立即执行。您的TableView可能目前未准备好进行测试。

我会尝试将.exists替换为waitForExistence()

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

您也可以放弃此XCTAssert

tap()将等待存在3秒钟,如果该单元格不存在,则会产生一条错误消息。

缩写为:

app.tables.cells.firstMatch.tap()

答案 3 :(得分:0)

我已经下载了您的演示。

虽然iPhone应用程序从“主视图”开始,但iPad应用程序具有“拆分视图”或“详细视图”(纵向)。

纵向视图应从“母版+详细视图”开始。更改UI测试的其他方法如下:

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