Harry Blue
Harry Blue

Reputation: 4502

Cannot access SwiftUI Text in XCTestCase UI Test

I am trying to add UI tests to a SwiftUI project.

I have a list, which contains views - those then contain a number of views.

I cannot seem to access the furthest most view in my UI test.

I thought I could add an accessibility identifier to each element but I cannot make my test pass still.

A very simple example;

ContentView

struct ListModel: Identifiable {
    let id: String
    let text: String
}

struct ContentView: View {
    
    private var state = (0..<50).map { ListModel(id: "\($0)", text: "Row \($0)") }
    
    var body: some View {
        List(state, id: \.id) { item in
            ContentViewRow(text: item.text)
                .accessibility(identifier: "FEED_ITEM")
        }
        .accessibility(identifier: "FEED")
    }
}

struct ContentViewRow: View {
    let text: String
    var body: some View {
        Text(text)
            .accessibility(identifier: "CONTENT_ROW_TEXT")
    }
}

Tests

class TestingSwiftUIUITests: XCTestCase {

    func testExample() throws {
        // UI tests must launch the application that they test.
        let app = XCUIApplication()
        app.launch()
        
        let feed = app.tables["FEED"]
        XCTAssert(feed.waitForExistence(timeout: 0.5))

        let row0 = feed.staticTexts["FEED_ITEM"].firstMatch
        XCTAssert(row0.waitForExistence(timeout: 0.5))
        
        let textView = row0.staticTexts["CONTENT_ROW_TEXT"].firstMatch
        XCTAssert(textView.waitForExistence(timeout: 0.5)) // <-- This fails.
    }
}

How can I access a view inside ContentViewRow - thank you.

Upvotes: 2

Views: 906

Answers (2)

laechoppe
laechoppe

Reputation: 250

identifier: CONTENT_ROW_TEXT seems to be overriden by identifier: FEED_ITEM

Perhaps you can leave just FEED_ITEM and check for the label text if needed.

let row0 = feed.staticTexts["FEED_ITEM"].firstMatch
XCTAssert(row0.waitForExistence(timeout: 0.5))
XCTAssert(row0.label == "Row 0")

Upvotes: 2

Ashi
Ashi

Reputation: 161

List UITestCase in SwiftUI

Swift UI View Code

struct ContentView: View {
    
    @StateObject var viewModel = ContentViewModel()
    
    var body: some View {
        NavigationView {
            List(viewModel.list, id: \.id) { item in
                NavigationLink(destination: self.detailsView(content: item)) {
                    ContentViewRow(text: item.text)
                        .accessibility(identifier: "FEED_ITEM_\(item.id)")
                }
            }
            .listStyle(.plain)
            .padding(.all, 20)
            .accessibility(identifier: "FEED")
            .onAppear {
                self.viewModel.publishListModel()
            }
            .navigationTitle("List")
        }
    }
    
    func detailsView(content: ListModel) -> some View {
        return ContestDetailsView(content: content)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct ContentViewRow: View {
    let text: String
    var body: some View {
        Text(text)
            .accessibility(identifier: "CONTENT_ROW_TEXT")
    }
}
struct ContestDetailsView: View {
    var content: ListModel
    
    var body: some View {
        Text(content.text)
            .navigationTitle("Details")
            .accessibility(identifier: "FEED_Details")
    }
}

UI Test Case

func testTableList() throws {
    // UI tests must launch the application that they test.
    let app = XCUIApplication()
    app.launch()
    
    let pred = NSPredicate(format: "identifier == 'FEED'")
    let tableView = app.descendants(matching: .any).matching(pred).firstMatch
    let arrOfTexts = tableView.staticTexts
    XCTAssertGreaterThan(arrOfTexts.count, 10)
    // let feed = app.tables["FEED"]
    XCTAssert(tableView.waitForExistence(timeout: 0.5))
    
    let row7 = tableView.staticTexts["FEED_ITEM_7"].firstMatch
    XCTAssert(row7.waitForExistence(timeout: 0.5))
    
    //let textView = row7.staticTexts["Row 7"].firstMatch
    //XCTAssert(textView.waitForExistence(timeout: 0.5)) // <-- This fails.
    
    row7.tap()
    let textView = app.staticTexts["Row 7"]
    // If the text ran successfully, the the above textView will be != Nil
    XCTAssertEqual(textView.exists, true)
}

Upvotes: 0

Related Questions