【问题标题】:XCTestCase - iOS UI Tests - dealing with UITableViews with many cellsXCTestCase - iOS UI 测试 - 处理具有许多单元格的 UITableViews
【发布时间】:2016-02-05 09:24:06
【问题描述】:

我正在试验(Xcode 7)UI XCTestCase 测试用例,我偶然发现了一个 UIView 的问题,其中我有一个包含许多单元格(4000+)的 UITableView。

应用正常运行时,只渲染可见单元格,完全没有性能问题。 但是,如果我在录制 XCTestCase 的上下文中运行应用程序并导航到此屏幕,则模拟器会冻结,显然是因为每个单元格都呈现为好像它是可见的。 如果我尝试手动编写导航脚本并运行 XCTestCase,则测试用例在导航到此屏幕后立即失败,并以“UI 测试失败 - 无法获得刷新的快照”退出,显然又是因为所有单元格都在被渲染而这没有及时完成。

我认为这与测试框架构建了显示屏幕的整个元模型有关,将 4000 多个单元格中的每一个都添加到视图树层次结构中。

我尝试添加一个期望,希望这会给测试容器足够的时间来完成所有单元格的渲染,但这不起作用。

有解决办法吗?是否有可能跳过构建 UI 树层次结构的一部分或其他什么? 我的目标是能够为此屏幕编写 UI 测试。

【问题讨论】:

  • 能否请您出示您的测试代码?谢谢
  • 这是一个自定义单元格,有 3 个标签和 2 个图像。我设法通过使用 [tableView indexPathsForVisibleRows] 解决它;确定单元格是可见的,如果它没有为我的测试返回 [UITableViewCell new],所以使用的单元格很简单,但这不是我想要的。我正在考虑以某种方式让用户界面树懒惰地构建,但我认为现在这不可能。如果可以将这些 xc ui 测试用例配置为没有如此严重的副作用(例如在这种情况下加载所有单元格),那就太好了。

标签: ios uitableview xctest xcode-ui-testing xctestcase


【解决方案1】:

我遇到了同样的问题,我同意不得不等待整个表加载是令人沮丧的,但这是我必须使用以下解决方法所做的。

这可能不是您想要的,但它可能会帮助其他人:

基本上,如果表格中的单元格不相等,我会连续计算 2 次,这意味着表格仍在加载。将它放在一个循环中并执行此操作,直到两个计数返回相同的数字,这意味着表已完成加载。然后我停止了 30 秒,如果这需要超过 30 秒,测试将失败(在我的情况下,这已经足够了)。如果您的桌子需要更长的时间,您可以将数字增加到 180 3 分钟等...

    let startTime = NSDate()
    var duration : TimeInterval
    var cellCount1 : UInt = app.tables.cells.count
    var cellCount2 : UInt = app.tables.cells.count
    while (cellCount1 != cellCount2) {
        cellCount1 = app.tables.cells.count
        cellCount2 = app.tables.cells.count
        duration = NSDate().timeIntervalSince(startTime as Date)
        if (duration > 30) {
            XCTFail("Took too long waiting for cells to load")
        }
    }
    //Now I know the table is finished loading and I can tap on a cell
    app.tables.cells.element(boundBy: 1).tap()

【讨论】:

    【解决方案2】:

    如果您可以使用firstMatch 而不是element,并且还可以避免使用count,您或许可以避免渲染整个表格。

    我进行了一项测试,检查表格前两个单元格中的预期标签。起初,我使用app.table.cells.element(boundBy: 0)app.table.cells.element(boundBy: 1) 来查找第一个和第二个单元格。这导致在我可以访问单元格之前呈现整个表格。

    我调整了我的测试,使其不太精确,但对我来说仍然足够好(考虑到否则会花费大量时间)。相反,我将matching 与预期标签值的谓词一起使用firstMatch,以查找符合我想要的条件的第一个单元格。这样,一旦找到它们,遍历就会停止(并且由于它们位于表的顶部,因此很快)。

    这是之前和之后的代码。

    之前(缓慢,但更精确):

    private func checkRhymes(query: String, expectedFirstRhyme: String, expectedSecondRhyme: String) {
        let table = app.tables.element
        let cell0 = table.cells.element(boundBy: 0)
        let cell1 = table.cells.element(boundBy: 1)
        let actualRhyme0 = cell0.staticTexts.matching(identifier: "RhymerCellWordLabel").firstMatch.label
        let actualRhyme1 = cell1.staticTexts.matching(identifier: "RhymerCellWordLabel").firstMatch.label
    
        XCTAssertEqual(expectedFirstRhyme, actualRhyme0, "Expected first rhyme for \(query) to be \(expectedFirstRhyme) but found \(actualRhyme0)")
        XCTAssertEqual(expectedSecondRhyme, actualRhyme1, "Expected first rhyme for \(query) to be \(expectedSecondRhyme) but found \(actualRhyme1)")
    }
    

    更快,但不太精确(但足够好):

    private func checkRhymes(query: String, expectedFirstRhyme: String, expectedSecondRhyme: String) {
        let table = app.tables.firstMatch
        let label0 = table.cells.staticTexts.matching(NSPredicate(format: "label = %@", expectedFirstRhyme)).firstMatch
        let label1 = table.cells.staticTexts.matching(NSPredicate(format: "label = %@", expectedSecondRhyme)).firstMatch
    
        // We query for the first cells that we find with the expected rhymes,
        // instead of directly accessing the 1st and 2nd cells in the table,
        // for performance issues.
        // So we can't add assertions for the "first" and "second" rhymes.
        // But we can at least add assertions that both rhymes are visible,
        // and the first one is above the second one.
        XCTAssertTrue(label0.frame.minY < label1.frame.minY)
        XCTAssertTrue(label0.isHittable)
        XCTAssertTrue(label1.isHittable)
    }
    

    参考: https://developer.apple.com/documentation/xctest/xcuielementquery/1500515-element

    当您期望查询结果时,使用元素属性访问查询结果 查询的单个匹配元素,但要检查多个 访问结果之前的模棱两可的匹配。元素属性 遍历您应用的可访问性树以检查多个匹配项 返回之前的元素,如果没有则当前测试失败 单个匹配元素。

    如果您明确知道会有一个 匹配元素,使用 XCUIElementTypeQueryProvider firstMatch 而是财产。 firstMatch 停止遍历您应用的可访问性 层次结构一旦找到匹配的元素,加速元素 查询解析。

    【讨论】:

    • 此解决方案适用于获取.label.frame、.exists 等属性。但是当点击元素时它不起作用,或者意图更多的任何其他情况而不是获得财产。
    猜你喜欢
    • 2014-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多