31

In Xcode UI testing, how do I test that staticTexts contains a string?

In the debugger, I can run something like this to print out all the content of staticTexts: po app.staticTexts . But how do I test if a string exists anywhere within all of that content?

I can check for the existence of each staticText doing something like app.staticTexts["the content of the staticText"].exists? but I have to use the exact content of that staticText. How can I use only a string which may be only a part of that content?

Vincent Phillips
  • 445
  • 1
  • 5
  • 9

5 Answers5

41

You can use NSPredicate to filter elements.

  let searchText = "the content of the staticText"
  let predicate = NSPredicate(format: "label CONTAINS[c] %@", searchText)
  let elementQuery = app.staticTexts.containing(predicate)
  if elementQuery.count > 0 {
    // the element exists
  }

With CONTAINS[c] you specify that the search is case insensitive.

Have a look at Apples Predicate Programming Guide

dnlkng
  • 829
  • 1
  • 10
  • 16
17

First, you need to set an accessibility identifier for the static text object you want to access. This will allow you to find it without searching for the string it is displaying.

// Your app code
label.accessibilityIdentifier = "myLabel"

Then you can assert whether the string displayed is the string you want by writing a test by calling .label on the XCUIElement to get the contents of the displayed string:

// Find the label
let myLabel = app.staticTexts["myLabel"]
// Check the string displayed on the label is correct
XCTAssertEqual("Expected string", myLabel.label)

To check it contains a certain string, use range(of:), which will return nil if the string you give is not found.

XCTAssertNotNil(myLabel.label.range(of:"expected part"))
Oletha
  • 7,324
  • 1
  • 26
  • 46
8

I had this problem while I was building my XCTest, I had a dynamic string inside of my block of text I should verify. I had built this two functions to solve my problem:

func waitElement(element: Any, timeout: TimeInterval = 100.0) {
    let exists = NSPredicate(format: "exists == 1")

    expectation(for: exists, evaluatedWith: element, handler: nil)
    waitForExpectations(timeout: timeout, handler: nil)
}

func waitMessage(message: String) {
    let predicate = NSPredicate(format: "label CONTAINS[c] %@", message)
    let result = app.staticTexts.containing(predicate)
    let element = XCUIApplication().staticTexts[result.element.label]
    waitElement(element: element)
}

I know this post is old, but I hope this can help someone.

J. Lopes
  • 1,336
  • 16
  • 27
  • 1
    Is there a specific reason why you first use `app` then you create a new instance with `XCUIApplication()`? – GrizzlyBear Jul 09 '19 at 07:51
  • XCUIApplication().staticTexts[result.element.label] could fail to resolve if the label with given text is not created at this point, how do you wait for it ? – Tomislav Kordic Apr 06 '23 at 15:23
4

You can create an extensions to simply use it over XCUIElement.

extension XCUIElement {
    
    func assertContains(text: String) {
        let predicate = NSPredicate(format: "label CONTAINS[c] %@", text)
        let elementQuery = staticTexts.containing(predicate)
        XCTAssertTrue(elementQuery.count > 0)
    }
}

Usage:

// Find the label
let yourLabel = app.staticTexts["AccessibilityIdentifierOfYourLabel"].firstMatch

// assert that contains value
yourLabel.assertContains(text: "a part of content of the staticText")

Omer Faruk Ozturk
  • 1,722
  • 13
  • 25
0
    // Encapsulate your code
    func yourElement() -> XCUIElement {
        let string = "The European languages are members of the same family."
        let predicate = NSPredicate(format: "label CONTAINS[c] '\(string)'")
        return app.staticTexts.matching(predicate).firstMatch
    }

    // To use it
    XCTAssert(yourPage.yourElement().waitForExistence(timeout: 20))
Zhou Haibo
  • 1,681
  • 1
  • 12
  • 32