从拆分视图控制器内部测试 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))
}
...
}
我正在测试 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))
}
...
}