how to properly error handle XCUI element not found in swift - ios

I am trying to write a do-try-catch in swift for my iOS UI test which uses XCUI testing. I am reading the error-handling section: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html#//apple_ref/doc/uid/TP40014097-CH42-ID508
but am unsure of which error should be thrown when an element is not found.
func tapElement(string:String) throws {
do{
waitFor(app.staticTexts[string], 5)
try app.staticTexts[string].tap()
}
catch {
NSLog("Element was not found: \(string)")
//how can i check specifically that the element was not found?
}
}
....
func waitFor(element:XCUIElement, seconds waitSeconds:Double) {
NSLog("Waiting for element: \(element)")
let exists = NSPredicate(format: "exists == 1")
expectationForPredicate(exists, evaluatedWithObject: element, handler: nil)
waitForExpectationsWithTimeout(waitSeconds, handler: nil)
}
any help greatly appreciated!!

You should use element.exists AND element.isHittable
element.exists checks whether the element is an element of the UIApplication/ScrollView/..
element.isHittable determines if a hit point can be computed for the element.
If you don't check for both, than element.tap() throws the following error, for example, if the element is under the keyboard:
Failed: Failed to scroll to visible (by AX action) TextField,...
Example code:
let textField = elementsQuery.textFields.allElementsBoundByIndex[i]
if textField.exists && textField.isHittable {
textField.tap()
} else {
// text field is not hittable or doesn't exist!
XCTFail()
}

You shouldn't need to try-catch finding elements in UI Testing. Ask the framework if the element exists() before trying to tap() it.
let app = XCUIApplication()
let element = app.staticTexts["item"]
if element.exists {
element.tap()
} else {
NSLog("Element does not exist")
}
Check out my blog post on getting started with UI Testing for more specific examples, like tapping an button.

Related

Handling the answer from API in UI Testing Swift

I have weather app. It fetches the data from API. I enter needed city, then next screen opens and shows me the name of the city and temperature. I am writing UI test, which should open the app, handle an alert which asks to use location, then test should write the city name and check if this city exists in the screen. All works except checking the city name at the end. I thought maybe the problem is because it needs some time to get the answer from API, and tests doesn’t wait for it. Maybe I need to set timer to wait for answer. Or the problem is in smth else?
Here is my code and it fails at the last line.
func testExample() throws {
let app = XCUIApplication()
app.launchArguments = ["enable-testing"]
app.launch()
app/*#START_MENU_TOKEN#*/.staticTexts["My location"]/*[[".buttons[\"My location\"].staticTexts[\"My location\"]",".staticTexts[\"My location\"]"],[[[-1,1],[-1,0]]],[0]]#END_MENU_TOKEN#*/.tap()
addUIInterruptionMonitor(withDescription: "Allow “APP” to access your location?") { (alert) -> Bool in
let button = alert.buttons["Only While Using the App"]
if button.exists {
button.tap()
return true // The alert was handled
}
return false // The alert was not handled
}
app.textFields["Enter your city"].tap()
app.textFields["Enter your city"].typeText("Barcelona")
app.buttons["Check weather"].tap()
XCTAssertTrue(app.staticTexts["Barcelona"].exists)
}
XCTest comes with a built-in function you need
Documentation: https://developer.apple.com/documentation/xctest/xcuielement/2879412-waitforexistence/
Example:
XCTAssertTrue(myButton.waitForExistence(timeout: 3), "Button did not appear")
I found the function and used it to wait before the result.
Here is the function and its usage in my code.
func waitForElementToAppear(_ element: XCUIElement) -> Bool {
let predicate = NSPredicate(format: "exists == true")
let expectation = expectation(for: predicate, evaluatedWith: element,
handler: nil)
let result = XCTWaiter().wait(for: [expectation], timeout: 5)
return result == .completed
}
app.textFields["Enter your city"].tap()
app.textFields["Enter your city"].typeText("Barcelona")
app.buttons["Check weather"].tap()
let result = app.staticTexts["Barcelona"]
waitForElementToAppear(result)
XCTAssertTrue(result.exists)

Xcode UI test XCTDaemonErrorDomain Code=13 while tapping on a cell

I have the following Xcode UI test code that used to pass just a week ago but now I get an error
let cellsQuery = app.cells.matching(identifier: "identifier")
XCTAssertGreaterThan(cellsQuery.count, 0)
let cell = cellsQuery.element(boundBy: 0)
expectElementToExist(cell)
cell.tap()
final func expectElementToExist(_ element: XCUIElement, exist: Bool = true, timeout: TimeInterval = 10) {
let predicate = NSPredicate(format: "exists == \(exist)")
expectation(for: predicate, evaluatedWith: element, handler: nil)
waitForExpectations(timeout: timeout, handler: nil)
XCTAssertEqual(element.exists, exist)
}
The error I get in cell.tap() is the below
Failure fetching attributes for element <XCAccessibilityElement: 0x604001a75240> pid: 5387, elementOrHash.elementID: 105553123110000.1888: Error Domain=XCTDaemonErrorDomain Code=13 "Error copying attributes -25202" UserInfo={NSLocalizedDescription=Error copying attributes -25202}
Any help would be much appreciated
Recently i also faced the similar type of issue after upgrading my project(Swift 3.2) into XCode 9.
The issue is
cell.isHittable is returning false.
Thats why default tap() method is not working for hierarchical elements.
I just did the below procedure and that worked fine.
Step 1: Create an extension like below
extension XCUIElement {
func tryClick() {
if self.isHittable {
self.tap()
}
else {
let coordinate: XCUICoordinate = self.coordinate(withNormalizedOffset: CGVector(dx:0.5, dy:0.5))
coordinate.doubleTap()
//coordinate.press(forDuration: 0.5)
}
}
}
Step 2: click on the element using the instance method created above instead of using direct tap() method.
cell.tryClick()
Note: try using
CGVector(dx:0.5, dy:0.5) or CGVector(dx:0.0, dy:0.0)
Hope it will solve your issue. It worked like a charm for me.

How do you pass a test with XCUITest? iOS Swift

So I have this XCTestCase, it runs a simple function and I'm satisfied with the results, but every time the function ends, it displays "TEST FAILED" and shut down the app.
One more thing, I tried to change the continueAfterFailure boolean to true and yet it still shut down the app after failure...
I couldn't find a solution yet, hope someone can help me :)
Thanks
Update:
Here's the code:
func loginSuccess (element: XCUIElement) {
//Entering invalid input to the text field
//App has validator a that doesn't let the input in
element.typeText("!##$%^")
XCTAssertTrue((element.value as? String == ""), "Test Passed!")
if element.value as? String == "" {
print ("Test Passed!") //This line works every run
} else {
XCTFail("Invalid text can be inserted") //Managed to force fail and succeded, but not pass.
}
}
You have things reversed. XCTAssertTrue evaluates that an expression is true and logs the provided message and fails the test if the expression is false.

How can I control the time after an UIButton has been tapped to prepare for next action in UI Testing with Xcode

I am using UI Test with Xcode 7 but have a few problems.
When I do record UI Test, Xcode translates Chinese to Unicode with uppercase 'U' and it shows errors.
And ui test code
XCUIApplication *app = [[XCUIApplication alloc] init];
[app.navigationBars[#"\u5934\u6761"].images[#"new_not_login30"] tap];
XCUIElementQuery *tablesQuery = app.tables;
[tablesQuery.cells.staticTexts[#"\u6211\u7684\u864e\u94b1"] tap];
the problem is: after tapping image, there is an animation showing sidebar with UITableView or showing UIAlertController but I cannot handle the duration time. Actually, within animation the testing continues to find next elements to match but these elements do not exist or generating. So the test always be failed.
Any solution to answer this question? Please help me. Thanks.
try it with expectationForPredicate. I don't know the syntax in objective-c. But here is a part of code in swift:
let app = XCUIApplication()
app.navigationBars["\u5934\u6761"].images["new_not_login30"].tap()
let label = app.cells.staticTexts["\u6211\u7684\u864e\u94b1"]
let exists = NSPredicate(format: "exists == 1")
expectationForPredicate(exists, evaluatedWithObject: label) {
// If the label exists, also check that it is enabled
if label.enabled {
label.tap()
return true
} else {
return false
}
}
waitForExpectationsWithTimeout(5) { error in
if (error != nil) { assertion ....}
}
Just translate this code in objective-c.
Cheers

How to check for the presence of static text displayed from the network in UI tests in Xcode?

I am using the UI test APIs introduced in Xcode 7 XCTest. On my screen I have a text that is loaded from the network.
The test fails if I simply check it with exists property.
XCTAssert(app.staticTexts["Text from the network"].exists) // fails
It does work though if I first send the tap or any other event to the text like this:
app.staticTexts["Text from the network"].tap()
XCTAssert(app.staticTexts["Text from the network"].exists) // works
It looks like if I just call exists it evaluates it immediately and fails because the text has not been downloaded from the network yet. But I think when I call the tap() method it waits for the text to appear.
Is there a better way to check for the presence of a text that is delivered from the network?
Something like (this code will not work):
XCTAssert(app.staticTexts["Text from the network"].eventuallyExists)
Xcode 7 Beta 4 added native support for asynchronous events. Here's a quick example of how to wait for a UILabel to appear.
XCUIElement *label = self.app.staticTexts[#"Hello, world!"];
NSPredicate *exists = [NSPredicate predicateWithFormat:#"exists == 1"];
[self expectationForPredicate:exists evaluatedWithObject:label handler:nil];
[self waitForExpectationsWithTimeout:5 handler:nil];
First create a query to wait for a label with text "Hello, world!" to appear. The predicate matches when the element exists (element.exists == YES). Then pass the predicate in and evaluate it against the label.
If five seconds pass before the expectation is met then the test will fail. You can also attach a handler block in that gets called when the expectation fails or times out.
If you're looking for more information regarding UI Testing in general, check out UI Testing in Xcode 7.
Swift 3:
let predicate = NSPredicate(format: "exists == 1")
let query = app!.staticTexts["identifier"]
expectation(for: predicate, evaluatedWith: query, handler: nil)
waitForExpectations(timeout: 5, handler: nil)
It will continuously check for 5 seconds whether that text is displayed or not.
As soon as it will find the text may be in less than 5 seconds, it will execute further code.
XCode9 has a method waitForExistence(timeout: TimeInterval) of XCUIElement
extension XCUIElement {
// A method for tap element
#discardableResult
func waitAndTap() -> Bool {
let _ = self.waitForExistence(timeout: 10)
let b = self.exists && self.isHittable
if (b) {
self.tap()
}
return b
}
}
// Ex:
if (btnConfig.waitAndTap() == true) {
// Continue UI automation
} else {
// `btnConfig` is not exist or not hittable.
}
But I encounter another problem, the element is exist, but not hittable. So I extend a method to wait an element to be hittable.
extension XCTestCase {
/// Wait for XCUIElement is hittable.
func waitHittable(element: XCUIElement, timeout: TimeInterval = 30) {
let predicate = NSPredicate(format: "isHittable == 1")
expectation(for: predicate, evaluatedWith: element, handler: nil)
waitForExpectations(timeout: timeout, handler: nil)
}
}
// Ex:
// waitHittable(element: btnConfig)
If I'm right in understanding you that the target text has already displayed when you're checking it's existing, you can try to use hittable property.

Resources