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

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.

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)

iOS UITests can't do basics actions after Swift 3 update

In my iOS i'm implementing UITests using XCUITest. It worked great since I had Swift 2.3, but after updating the app to Swift 3 basic actions like tap() don't work anymore.
Just a simple code that doesn't work anymore:
XCUIApplication().buttons["orgMenu"].tap()
throws
Assertion Failure: <unknown>:0: UI Testing Failure - Failure getting snapshot Error Domain=XCTestManagerErrorDomain Code=9 "Error -25204 getting snapshot for element <AXUIElement 0x7f8297d15a50> {pid=32375}" UserInfo={NSLocalizedDescription=Error -25204 getting snapshot for element <AXUIElement 0x7f8297d15a50> {pid=32375}}
The name of the button is correct: if I record the test and tap the button the line above is exactly what I get.
The button is in the view since I'm waiting for the existence of it (tried both manually, through a breakpoint, and programmatically with this:
let exists = NSPredicate(format: "exists == 1")
expectation(for: exists, evaluatedWith: XCUIApplication().buttons["orgMenu"], handler: nil
waitForExpectations(timeout: time, handler: nil)
)
And anyway, it worked before Swift 3.
Any idea? Thanks in advance!
I'll just answer to my own question, since I found a workaround.
Since Swift3, for some reason, UI tests are not able to manage views with a loadMore pattern. If I have in my view a TableView and somewhere in the code a manage the loadMore pattern manually, and in a test I tap a button, the loadMore is called in a sort of infinite loop, and that overheads the app's resources, making the test fail.
Workaround: just deactivate any loadMore if a UI Test is running.
In the tests' setup:
override func setUp() {
super.setUp()
let app = XCUIApplication()
continueAfterFailure = false
app.launchEnvironment = ["isUITest":"YES"]
app.launch()
}
In the view with the loadData:
-(void)loadMore
{
if ([Utils isRunningUITest]) {
return;
}
// My actual function.
}
and my Utils
+(BOOL)isRunningUITest {
NSDictionary *environment = [[NSProcessInfo processInfo] environment];
return environment[#"isUITest"];
}
Sorry for Swift and Objective-C mix, hope this is helpful to someone.
You can try and do something like:
let app = XCUIApplication()
XCTAssert(app.buttons["orgMenu"].waitForExistence(timeout: 10))
app.buttons["orgMenu"].tap()

XCTest is not giving me accurate results each time (Sometimes it fails, sometimes it passes)

I am using XCode 8.2.1 (Swift) with XCode UI tests to test my app in terms of UI, so my problem with this tool is it doesn't give me accurate results some tests are passed and at the next time they fail.
My question might be generic but is this problem related to me or is it common one? i believe this happened after migrating to XCode 7+
Do you guys recommend using anther tool for UI tests other than this one? as am looking for faster, accurate and ability to integrate with a CI server.
private func waitForElementToAppear(element: XCUIElement,
file: String = #file, line: UInt = #line) {
let existsPredicate = NSPredicate(format: "exists == true")
expectation(for: existsPredicate,
evaluatedWith: element, handler: nil)
waitForExpectations(timeout: 30) { (error) -> Void in
if (error != nil) {
let message = "Failed to find \(element) after 30 seconds."
self.recordFailure(withDescription: message,
inFile: file, atLine: line, expected: true)
}
}
}
invoke this method and avoid test fails because sometimes our UI takes more time to getting result from Server.

how to properly error handle XCUI element not found in swift

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.

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