Search code examples
iosxcode-ui-testinguitest

Is there a way to use valueForKey with NSPredicate in UITest?


I have the following test helper function for my iOS UI tests:

func waitForElementToHaveKeyboardFocus(element: XCUIElement) {
    self.expectationForPredicate(NSPredicate(format:"valueForKey(\"hasKeyboardFocus\") == true"), evaluatedWithObject:element, handler: nil)
    self.waitForExpectationsWithTimeout(5, handler: nil)
}

In my test I have:

let usernameTextField = app.textFields["Username"]
let passwordTextField = app.secureTextFields["Password"]
waitForElementToHaveKeyboardFocus(usernameTextField)

The test fails with the following error:

error: -[ExampleAppUITests.ExampleAppUITests testExampleApp] : failed: caught "NSUnknownKeyException", "[<_NSPredicateUtilities 0x10e554ee8> valueForUndefinedKey:]: this class is not key value coding-compliant for the key hasKeyboardFocus."

If I put a breakpoint in the test at the failure and manually call valueForKey("hasKeyboardFocus") on both the focused and unfocused fields I seem to get the correct behavior:

(lldb) po usernameTextField.valueForKey("hasKeyboardFocus")
    t =    51.99s     Find the "Username" TextField
    t =    51.99s         Use cached accessibility hierarchy for ExampleApp
    t =    52.00s         Find: Descendants matching type TextField
    t =    52.01s         Find: Elements matching predicate '"Username" IN identifiers'
▿ Optional<AnyObject>
  - Some : 1

(lldb) po passwordTextField.valueForKey("hasKeyboardFocus")
    t =   569.99s     Find the "Password" SecureTextField
    t =   569.99s         Use cached accessibility hierarchy for ExampleApp
    t =   570.01s         Find: Descendants matching type SecureTextField
    t =   570.01s         Find: Elements matching predicate '"Password" IN identifiers'
▿ Optional<AnyObject>
  - Some : 0

Is it possible to make valueForKey on a XCUIElement work with NSPredicate in a UI test? Is there another elegant way to do this?


Solution

  • It looks like your predicate is slightly off. Try changing it to the following:

    NSPredicate(format: "hasKeyboardFocus == true"), evaluatedWithObject:element, handler: nil)
    

    You don't need to pass in the valueForKey part when creating the predicate.