Hi,
Recently I discovered an odd behaviour with Xocde 9.1 / iOS 11.1 UI tests.
Running a simple UI tests to navigate between screens will randomly fail based on the simulator in use. For example, running the UI tests on an iPhone 6s simulator will fail most if not all the time on simple checks like the `exists` and `isHittable` properties on `XCUIElement`. While runnining on iPhone 8+ simulator appears to work most of the time. Running the same tests on iOS 10 runtime reliably passes.
The current workaround I found is to use `waitForExistence` to wait for the next screen to appear before performing any checks. This however isn't ideal as it would mean all tests would require a wait after any action which is not very scalable nor desirable (what if the chosen timeout isn't enough?)
Perhaps I am missing something obvious ? Has anyone else encountered this? I've filed a radar (35334066).
Thanks
Sample App:
- UINavigatioController with two screens , Screen 1 & Screen 2
- Screen 1 has a UIBarButtonItem titled "Push" that pushes Screen 2
Sample Tests:
import XCTest
class NavigationTests: XCTestCase {
var app: XCUIApplication!
override func setUp() {
super.setUp()
continueAfterFailure = false
app = XCUIApplication()
app.launch()
}
override func tearDown() {
app = nil
super.tearDown()
}
func testNavigaitonBetweenScreens() {
XCTAssertTrue(screen1.exists)
XCTAssertTrue(screen1.isHittable)
pushScreen2.tap()
// Checking the current screen no longer exists
// will fail randomly depending on the simulator in use
// e.g. most failures occur on iPhone 6s
XCTAssertFalse(screen1.exists)
XCTAssertTrue(screen2.exists)
XCTAssertTrue(screen2.isHittable)
backToScreen1.tap()
XCTAssertFalse(screen2.exists)
XCTAssertTrue(screen1.exists)
XCTAssertTrue(screen1.isHittable)
}
func testNavigaitonBetweenScreens2() {
XCTAssertTrue(screen1.exists)
XCTAssertTrue(screen1.isHittable)
pushScreen2.tap()
// Check the destination screen existing first
// makes a difference
XCTAssertTrue(screen2.exists)
// However may not pass the hittable check depending
// on the simulator in use
// e.g. most failures occur on iPhone 6s
XCTAssertTrue(screen2.isHittable)
XCTAssertFalse(screen1.exists)
backToScreen1.tap()
XCTAssertTrue(screen1.exists)
XCTAssertTrue(screen1.isHittable)
XCTAssertFalse(screen2.exists)
}
func testNavigaitonBetweenScreensWorkaround() {
XCTAssertTrue(screen1.exists)
XCTAssertTrue(screen1.isHittable)
pushScreen2.tap()
XCTAssertTrue(screen2.waitForExistence(timeout: 2))
XCTAssertFalse(screen1.exists)
XCTAssertTrue(screen2.exists)
XCTAssertTrue(screen2.isHittable)
backToScreen1.tap()
XCTAssertTrue(screen1.waitForExistence(timeout: 2))
XCTAssertFalse(screen2.exists)
XCTAssertTrue(screen1.exists)
XCTAssertTrue(screen1.isHittable)
}
// MARK: - Helpers
var pushScreen2: XCUIElement {
return app.navigationBars.buttons["Push"]
}
var screen1: XCUIElement {
return app.otherElements["screens.sc1"]
}
var screen2: XCUIElement {
return app.otherElements["screens.sc2"]
}
var backToScreen1: XCUIElement {
return app.navigationBars.buttons["Screen 1"]
}
}