无法在 Xcode UI 测试中点击 UIBarButtonItem

Posted

技术标签:

【中文标题】无法在 Xcode UI 测试中点击 UIBarButtonItem【英文标题】:Cannot tap UIBarButtonItem in Xcode UI Test 【发布时间】:2015-12-23 00:28:08 【问题描述】:

我的导航栏上有一个“添加”按钮,我需要让 Xcode 的 UI 测试点击该按钮以在它打开的视图控制器中执行测试。我像这样以编程方式添加按钮:

UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(showAddVC)];
self.navigationItem.rightBarButtonItem = addButton;

在我的测试中,我有:

XCUIApplication *app = [[XCUIApplication alloc] init];
XCTAssert([app.buttons[@"Add"] exists]); // <-- This passes, so the test runner does see the button.

但是当我尝试使用以下任一方法点击它时:

// Generated using the test recorder
[app.navigationBars[@"App Title"].buttons[@"Add"] tap];

或:

// Same expression used with the XCTAsset earlier
[app.buttons[@"Add"] tap];    

什么都没有发生。当点击按钮时应该发生的动作不会发生。我尝试在行之间添加一些sleep(5) 以让应用程序加载,但这并没有太大帮助。

这是测试日志:

Test Case '-[xx]' started.
    t =     0.00s     Start Test
    t =     0.00s     Set Up
2015-12-22 16:25:02.898 XCTRunner[10978:384690] Continuing to run tests in the background with task ID 1
    t =     0.94s         Launch xx
    t =     1.01s             Waiting for accessibility to load
    t =     3.45s             Wait for app to idle
    t =     9.02s     Tap "Add" Button
    t =     9.02s         Wait for app to idle
    t =    39.07s             Assertion Failure: UI Testing Failure - App failed to quiesce within 30s
xx: error: -[xx] : UI Testing Failure - App failed to quiesce within 30s

【问题讨论】:

日志告诉你主线程上发生了其他事情,因此无法点击按钮。你需要思考为什么会这样。 我将如何进行测试?暂停调试器显示线程 1 只是 main.m 调用。 断言失败可能是由框架中的错误引起的。试试这个——在你点击添加按钮之前,试着点击屏幕上的任何地方——例如导航栏本身。然后在该点击语句之后的行上放置一个断点。当您运行测试并到达该断点时,您可以在 LLDB 中打印出视图层次结构。确认 LLDB 中存在 Add 按钮。根据上面的错误陈述,我认为 XCTest 能够看到“添加”按钮 - 我不认为这是让你失望的错误。 【参考方案1】:

以上答案都不适合我。经过数小时的努力,最终使它起作用的是重复敲击。试试这个:

[app.navigationBars[@"App Title"].buttons[@"Add"] tap];
[app.navigationBars[@"App Title"].buttons[@"Add"] tap];

虽然上述方法最初对我有用,但我发现有时第一次点击会起作用,这会导致两次点击。我对此的解决方案是点击在 UI 测试开始时不会触发任何操作的任意 UI 元素,然后照常进行。我认为第一次点击可以在某些设备上运行,或者可能在第一次 UI 测试运行之后。

【讨论】:

我应该在 48 小时前遇到这个答案!【参考方案2】:

在您的情况下,测试 exists 似乎还不够。等待按钮变为hittable,然后再尝试点击它。

    expectationForPredicate(predicate, evaluatedWithObject: element, handler: nil)
    waitForExpectationsWithTimeout(timeoutSeconds, handler: nil)

在你的情况下它会在哪里:

    expectationForPredicate(NSPredicate(format: "hittable == YES"), evaluatedWithObject: [app.buttons[@"Add"], handler: nil)
    waitForExpectationsWithTimeout(15, handler: nil)
    [app.buttons[@"Add"] tap];  

这将暂停waitForExpectationWithTimeout 之后的代码执行,直到该谓词满足给定元素为止。

否则,在极端情况下,我发现在尝试与某些组件交互时有时会出现错误。这些如何、为什么以及何时发生有点神秘,但它们似乎与某些组件有些一致,而且涉及 UINavigationBars 的事情似乎更经常发生。

为了克服这些问题,我发现使用这个扩展有时会奏效。

extension XCUIElement 

    /* Sends a tap event to a hittable/unhittable element. Needed to get past bug */
    func forceTapElement() 
        if hittable 
            tap()
        
        else 
            let coordinate: XCUICoordinate = coordinateWithNormalizedOffset(CGVectorMake(0.0, 0.0))
            coordinate.tap()
        
    

【讨论】:

我收到一个错误@alex 抓到 "NSInvalidArgumentException", "-[XCUIElement forceTapElement]: unrecognized selector sent to instance 0x7fc8451fb140" 我对扩展做错了吗? @ChrisButcher 如果您正确声明它应该可以工作,您是在 UITest 文件的文件范围内声明它吗? 是 @alex 在文件范围内。 .... import XCTest extension XCUIElement func forceTapElement() if self.hittable self.tap() else let coordinate: XCUICoordinate = self.coordinateWithNormalizedOffset(CGVectorMake(0.0, 0.0)) coordinate.tap() class MoreMenuTests2: XCTestCase .... 似乎没有押韵或理由。有时“存在”只是错误的,即使你可以清楚地看到元素在那里。我已经多次运行相同的 UI 测试。它要么全部运行良好并通过,要么存在检查在不同运行中对不同元素失败。 XCTest 中显然还有一些问题需要苹果解决。 我用 + 栏按钮遇到了同样的情况。 waitForExpectationsWithTimeout 没有帮助。按钮是可点击的并且存在,但单击不起作用。但是连续两次敲击有效!所以我最终得到了这个解决方法 - 在单击不起作用的地方添加两个水龙头。为这个测试寻找解决方法杀死了两天。【参考方案3】:

对于亚历克斯的答案不起作用的人,试试这个:

extension XCUIElement 
    func forceTap() 
        coordinate(withNormalizedOffset: CGVector(dx:0.5, dy:0.5)).tap()
    

我刚刚遇到了UIWebView 的问题,它是可以点击的,但是在通过坐标完成之前,点击不起作用

【讨论】:

【参考方案4】:

一种可能性是您的条形按钮项目推动了一个具有刷新控件的新视图控制器 - 您可能会在 iPad 上特别遇到这种情况,仅当您的条形按钮项目推动到拆分视图控制器的细节部分时。获取项目后,您可能会停止刷新控件,该控件甚至不会刷新。 Xcode 的自动化确实会对此大发雷霆,而睡眠/超时对此无济于事。

因此,要解决此问题,在您的主应用程序上,请务必在结束刷新之前检查您的刷新控件是否正在刷新。我还想首先检查它不是 nil:如果您使用的是 Swift,则可以一举检查:

if let refresh = self.refreshControl, refresh.refreshing 
  refresh.endRefreshing()

【讨论】:

以上是关于无法在 Xcode UI 测试中点击 UIBarButtonItem的主要内容,如果未能解决你的问题,请参考以下文章

在 XCode 中的 UI 测试期间无法访问自定义视图

如何在 Xcode10 Ui 测试中点击标签的特定单词?

如何在 Xcode UI 测试脚本中执行点击和拖动?

Xcode UI 测试 - 如何在 WebView 中查找和点击()元素

Xcode UI 测试无法编辑多个 UITextFields

UI 测试 xCode 7.3 - 无法在控制台中打印变量(即“po app”):(无法构建 Objective-C 模块“Bolts”)