Diego Freniche
Diego Freniche

Reputation: 5414

How can I write UI tests for a SwiftUI InputField

I have this SwiftUI view (will remove non-relevant parts)

struct LoginView: View {
    
    var body: some View {
        VStack {
            Spacer()
            InputField(title: "Email", text: $email)
            InputField(title: "Password", text: $password, showingSecureField: true)
            InputField(title: "Store", text: $userStore)
            CallToActionButton(
                title: newUser ? "Register User" : "Log In",
                action: userAction
            )
            Button(action: { newUser.toggle() }) {
                HStack {
                    CheckBox(title: "Create Account", isChecked: $newUser)
                    Spacer()
                }
            }
            Spacer()
        }
    }

I can write a UI Test for that button with this code:

    func testClickLoginButton() throws {
        let app = XCUIApplication()
        app.launch()
                  
        let startButton = app.buttons["Log In"]
        XCTAssertTrue(startButton.waitForExistence(timeout: 1))
        XCTAssertTrue(startButton.isEnabled)
        startButton.tap()
    }

And it works, as there's a button with that title on screen.

But then I want to automatically put some text in those InputTexts and test them, but can't find them, as they're neither UITextField nor UITextView. How can I accomplish this? Thanks!

    func testAddEmail() throws {
        let app = XCUIApplication()
        app.launch()

        // fails as can't find any text field containing "Email"
                           
        let emailText = app.textFields["Email"]
        XCTAssertTrue(emailText.waitForExistence(timeout: 1))
        emailText.typeText("[email protected]")
        XCTAssertEqual(emailText.value as! String, "[email protected]")
    }

Upvotes: 2

Views: 1233

Answers (2)

multitudes
multitudes

Reputation: 3525

I am not an expert and have seen different ways to do UITests in SwiftUI. There are perhaps better ways with snapshots etc but I quickly recreated your example for fun and added an accessibility identifier:

struct InputField: View {
    var title: String
    @Binding var email: String

    var body: some View {
        VStack(alignment: .leading) {
            Text("Enter your email ")
            TextField(title, text: $email)
                .accessibility(identifier: "Email")

        }
        .padding()
    }
}

struct ContentView: View {
    @State private var email: String = ""

    var body: some View {
            InputField(title: "Email: ", email: $email)
    }
}

Also it might be sometimes required to add a tap before entering the email or text. It was giving me an error because of this.
The UItest would look like this and is passing :)

    func testAddEmail() throws {
        let app = XCUIApplication()
        app.launch()

        let emailText = app.textFields["Email"]
        XCTAssertTrue(emailText.waitForExistence(timeout: 1))
        emailText.tap() 
        emailText.typeText("[email protected]")

        XCTAssertEqual(emailText.value as! String, "[email protected]")
    }

Also a cool trick I learned is to put a breakpoint like this:

enter image description here

and in the debugger at the prompt enter the following:

(lldb) po XCUIApplication().textFields

enter image description here

And so you get the list and you see if identifiers are missing. Same thing for buttons and staticTexts etc.

Upvotes: 2

Parrarro64
Parrarro64

Reputation: 126

How about adding:

 InputField(title: "Email", text: $email)
        .accessibility(identifier: "email_input")

Same for any other element you want to test from UI Tests?

Upvotes: 3

Related Questions