Is there a way to clear/refresh the accessibility hierarchy cache
Asked Answered
A

4

13

I have a UI test that checks the value of static text element, waits a few seconds and checks again to confirm a change. At first it wasn't working because the hierarchy was not updating. I noticed this in the log;

Use cached accessibility hierarchy for

I've put in a workaround for this by simply adding a tap to a menu and opening/closing it so that an event is synthesized and the hierarchy is updated.

It would be better, however, if there was a way to clear the cache directly or force and update. I haven't found one in the API. Am I missing something?

Any ideas?

this is what I am doing;

XCTAssertEqual(app.staticTexts["myText"].label, "Expected 1")
sleep(20)
menu.tap()
sleep(1)
menu.tap()
XCTAssertEqual(app.staticTexts["myText"].label, "Expected 2")

What I'd like to be able to do it

XCTAssertEqual(app.staticTexts["myText"].label, "Expected 1")
sleep(20)
app.elements.refresh()
XCTAssertEqual(app.staticTexts["myText"].label, "Expected 2")
Adelric answered 16/12, 2015 at 19:28 Comment(3)
Can you include the code you're using? Are you using expectationForPredicate and waitForExpectationsWithTimeout to do the waiting, or something else?Twentyfour
Having said that, I came here due to a similar problem. I'm doing myButton.tap() which moves the button on screen, and afterwards myButton mysteriously refers to a different button, perhaps due to caching magicTwentyfour
added code above. In your case I think you have the opposite problem. You tap a button which synthesizes an event and, thus, reloads the element hierarchy so the cache is cleared when you tap your button.Adelric
H
8

In order to force an update of the accessibility hierarchy, request the count property for any XCUIElementQuery:

// refresh
_ = XCUIApplication().navigationBars.count

// examine
print(XCUIApplication().debugDescription)

The above results in: "Get number of matches for: Descendants matching type NavigationBar" and "Snapshot accessibility hierarchy for com.myapp".

Harem answered 28/9, 2016 at 9:25 Comment(2)
Thank you. I am using this helper to make it handy (done as category on XCUIElement): [[[[XCUIApplication alloc] init] childrenMatchingType:XCUIElementTypeAny] count];Additional
It didn't work for me (Xcode9.3). Normally all my views are updated in the element tree except very few ones which are custom views. I guess the dev has not called some super method in some of overrides in custom view subclass which updates this elements presence in the tree. This is just a guess though. My workaround tod reliably deal with such elements is to navigate back and reopen same screen, and now the element tree reflects the actual elements on screen.Chauncey
L
2

The following works for me in Xcode 10.2 (10E125):

import XCTest

extension XCUIApplication {
    // WORKAROUND:
    // Force XCTest to update its accessibility cache. When accessibility data
    // like NSObject.accessibility{Label|Identifier} changes, it takes a while
    // for XCTest to catch up. Calling this method causes XCTest to update its
    // accessibility cache immediately.
    func updateAccessibilityCache() {
        _ = try? snapshot()
    }
}
Lianne answered 3/4, 2019 at 15:48 Comment(0)
T
0

You should use expectationForPredicate, along the lines of...

let myText = app.staticTexts["myText"]
let waitFor = NSPredicate(format: "label = 'Expected 2'")
label.tap()
self.expectationForPredicate(waitFor, evaluatedWithObject: myText, handler: nil)
self.waitForExpectationsWithTimeout(2.0, handler: nil)

This will wait until either myText's label is 'Expected 2', or the timeout of 2 seconds is reached.

Twentyfour answered 18/12, 2015 at 5:10 Comment(1)
The problem is that as of Xcode 8.0 (iOS 10), XCUI will often crash if you attempt to waitForExpectationsWithTimeout for a navigation bar (or any of its children), while navigation controller transition has not yet completed. You have to first waitForExpectationsWithTimeout for any other UI element, such as a displayed scroll view or a button, and only then re- XCUIElementQuery for a navigation bar.Harem
W
0

In my case, it is a problem because I'm trying to test for Facebook login, which uses Safari controller. It looks like Facebook has updated the UI after cache.

So you need to wait a bit, use the wait function here https://mcmap.net/q/117746/-delay-wait-in-a-test-case-of-xcode-ui-testing

wait(for: 2)
let _ = app.staticTexts.count

But the above is just workaround and very flaky. A more correct approach would be to wait for a certain element to appear, see https://mcmap.net/q/717638/-swift2-ui-test-wait-for-element-to-appear

Warbler answered 30/5, 2017 at 10:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.