在 UI 测试 iOS 应用程序时禁用等待空闲状态

Disabling waiting for idle state in UI testing of iOS apps

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

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

但我认为可能有一些方法可以关闭 and/or 缩短状态“等待应用闲置”。在那儿?如何?还有其他解决方法吗?

不幸的是,使用 Apple 的 UI 测试无法打开 'wait for app to idle' 或轮询其他网络 activity,但是您可以使用环境变量禁用应用中的动画来进行测试更稳定。在测试之前的设置方法中设置这样的环境变量。

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

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

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

您所要做的就是调出为使应用程序空闲而调用的方法,并且不要对其进行操作。该方法是XCUIApplicationProcess waitForQuiescenceIncludingAnimationsIdle:

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

延长 XCTestCase class。我会打电话给我 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 将不再出现在日志中。

我在 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
}

我翻译成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
}

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

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

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