Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disabling waiting for idle state in UI testing of iOS apps

Basically the problem is the same as this one: XCTestCase: Wait for app to idle

I am using perpetually repeating "background animations" in my views. The UI testing of Xcode/iOS wants to wait for all UIView animations to end before it considers the app idle and goes on with stuff like tapping buttons etc. It just doesn't work with the way we've designed the app(s). (Specifically, we have a button that is animated with UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse options, so it never stops.)

But I'm thinking there might be some way to turn off and/or shorten the state "Wait for app to idle". Is there? How? Is there any other way around this?

like image 679
Jonny Avatar asked Dec 22 '16 06:12

Jonny


2 Answers

Unfortunately using Apple's UI Testing you can't turn 'wait for app to idle' or poll other network activity, however you can use environment variables to disable animations in your app to make the tests more stable. In your setup method before your test set an environment variable like this.

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

Now in your source code:

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

You can place that check in a specific view if you only want it to disable animations for that specific view or in a delegate file to disable animations throughout the app.

like image 171
h.w.powers Avatar answered Sep 27 '22 21:09

h.w.powers


You actually can disable wait for app to idle. This is a hack and may not be stable. With animations disabled, and this hack enabled, I am seeing about a 20% performance gain (on top of the performance boost from disabling animations).

All you have to do is swizzle out the method that is called to idle the app and no-op it. That method is XCUIApplicationProcess waitForQuiescenceIncludingAnimationsIdle:

Here is my working solution in swift 3 - there is likely a better way but this works for a proof of concept.

Extend the XCTestCase class. I'll call mine 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
}

Note wait for app to idle will no longer appear in the logs.

like image 24
gh123man Avatar answered Sep 27 '22 22:09

gh123man