在 iOS 应用的 UI 测试中禁用等待空闲状态

Posted

技术标签:

【中文标题】在 iOS 应用的 UI 测试中禁用等待空闲状态【英文标题】:Disabling waiting for idle state in UI testing of iOS apps 【发布时间】:2016-12-22 06:30:19 【问题描述】:

基本上问题和这个一样:XCTestCase: Wait for app to idle

我在我的视图中使用永久重复的“背景动画”。 Xcode/ios 的 UI 测试希望等待所有 UIView 动画结束,然后才认为应用程序空闲并继续进行点击按钮等操作。它只是不适用于我们设计应用程序的方式. (具体来说,我们有一个带有UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse 选项的动画按钮,因此它永远不会停止。)

但我认为可能有某种方法可以关闭和/或缩短“等待应用空闲”状态。有没有?如何?有没有其他办法解决这个问题?

【问题讨论】:

【参考方案1】:

您实际上可以禁用wait for app to idle。这是一个 hack,可能不稳定。在禁用动画并启用此 hack 的情况下,我看到了大约 20% 的性能提升(除了禁用动画带来的性能提升之外)。

您所要做的就是调出被调用以使应用程序空闲并且不对其进行操作的方法。那个方法是XCUIApplicationProcess waitForQuiescenceIncludingAnimationsIdle:

这是我在 swift 3 中的工作解决方案 - 可能有更好的方法,但这适用于概念验证。

扩展XCTestCase 类。我会打电话给我的MyTestCase

static var swizzledOutIdle = false

override func setUp() 
    if !MyTestCase.swizzledOutIdle  // ensure the swizzle only happens once
        let original = class_getInstanceMethod(objc_getClass("XCUIApplicationProcess") as! AnyClass, Selector(("waitForQuiescenceIncludingAnimationsIdle:")))
        let replaced = class_getInstanceMethod(type(of: self), #selector(MyTestCase.replace))
        method_exchangeImplementations(original, replaced)
        MyTestCase.swizzledOutIdle = true
    
    super.setUp()


@objc func replace() 
    return

注意wait for app to idle 将不再出现在日志中。

【讨论】:

像魅力一样工作!此外,您可能会提到,因此在某些情况下,有必要使用waitForExistence 函数——因为速度;) 天哪,这太棒了。测试有 80 年代的“等待应用程序空闲”废话,现在它可以在 3 秒内完成我希望它做的事情 在带有 XCode 11.1 的 iOS 13.1 iPad 上运行良好,谢谢。 @gh123man 有没有办法为 android 做同样的事情 我遇到了同样的问题,我们的一个 UI 测试卡在空闲状态大约 1 分钟,并继续执行以下测试步骤。这修复了我们的测试。但是如果禁用所有动画,我们怎么知道UI测试中是否有动画失败?【参考方案2】:

不幸的是,使用 Apple 的 UI 测试您无法打开“等待应用空闲”或轮询其他网络活动,但是您可以使用环境变量来禁用应用中的动画以使测试更加稳定。在测试之前的设置方法中,设置一个像这样的环境变量。

override func setUp() 
    super.setUp()
    continueAfterFailure = false
    let app = XCUIApplication()
    app.launchEnvironment = ["UITEST_DISABLE_ANIMATIONS" : "YES"]
    app.launch()

现在在你的源代码中:

if (ProcessInfo.processInfo.environment["UITEST_DISABLE_ANIMATIONS"] == "YES") 
    UIView.setAnimationsEnabled(false)

如果您只希望它禁用特定视图的动画或在委托文件中禁用整个应用程序的动画,则可以将该检查放置在特定视图中。

【讨论】:

谢谢,这就是我现在要做的。这并不完美,但可能是目前唯一的方法。可以告诉动画只运行一次迭代等。 澄清一下,应该是let app = XCUIApplication() app.launchEnvironment = ["UITEST_DISABLE_ANIMATIONS" : "YES"] app.launch() 我一直试图弄清楚发生了什么,这就是问题所在,谢谢!!【参考方案3】:

如果有人需要,我在 Objective-C 中使用了 gh123man 答案:

- (void)disableWaitForIdle 

    SEL originalSelector = NSSelectorFromString(@"waitForQuiescenceIncludingAnimationsIdle:");
    SEL swizzledSelector = @selector(doNothing);

    Method originalMethod = class_getInstanceMethod(objc_getClass("XCUIApplicationProcess"), originalSelector);
    Method swizzledMethod = class_getInstanceMethod([self class], swizzledSelector);

    method_exchangeImplementations(originalMethod, swizzledMethod);



- (void)doNothing 
    // no-op

【讨论】:

【参考方案4】:

我在几个测试类的 setUp() 中使用了 gh123man 的解决方案,在更新到 iOS 13.3 之前,它的效果非常好。从那时起,应用程序就陷入了启动状态。

发现如果我将其移至诸如 disableWaitForIdle() 和 enableWaitForIdle() 之类的方法并仅以最精细的方式调用它们(在我知道应用程序永远不会空闲的点击之前和之后),它仍然有效,例如像这样:

@discardableResult func selectOption() -> Self 
    disableWaitForIdle()
    app.cells["Option"].firstMatch.waitAndForceTap(timeout: 20)
    enableWaitForIdle()
    return self

【讨论】:

这并没有提供问题的答案。您可以search for similar questions,或参考页面右侧的相关和链接问题找到答案。如果您有一个相关但不同的问题,ask a new question,并包含指向此问题的链接以帮助提供上下文。见:Ask questions, get answers, no distractions 感谢您对 U10-Forward 的评论。我找到了一种让它工作的方法并更新了我的答案。【参考方案5】:

我翻译成 Objective-C,并成功使用了 h.w.powers 的 Swift 解决方案,以防万一有人需要。

设置:

XCUIApplication *app = [[XCUIApplication alloc] init];
app.launchEnvironment = @@"UITEST_DISABLE_ANIMATIONS":@"YES"; 
[app launch];

然后在你的代码中

if ([[[NSProcessInfo processInfo] environment][@"UITEST_DISABLE_ANIMATIONS"] isEqualToString:@"YES"]) 
// do something like stopping the animation

【讨论】:

以上是关于在 iOS 应用的 UI 测试中禁用等待空闲状态的主要内容,如果未能解决你的问题,请参考以下文章

XCUITest 等待未经测试的应用程序达到空闲状态

空闲资源忙时测试 UI

XCTestCase:等待应用空闲

UI 测试不再适用于 Xcode 7.3

应用程序状态更改时禁用 UI 的标准方法

在 UI 测试期间测试对象状态 (iOS)