DEV Community

Cover image for XCUITest Form Testing: Automating Text Fields, Pickers and Keyboards
QAPulse by SK
QAPulse by SK

Posted on Originally published at skakarh.com

XCUITest Form Testing: Automating Text Fields, Pickers and Keyboards

XCUITest Form Testing is essential for validating iOS forms where users enter text, select values, interact with keyboards, and submit structured data. Reliable form automation must verify not only that controls accept input, but also that validation, focus, keyboard behavior, pickers, and submission states work correctly.

What is XCUITest Form Testing?

XCUITest form testing is the process of automating and validating iOS forms with XCUITest, including text fields, secure fields, pickers, switches, buttons, keyboards, validation messages, and form submission workflows.

A typical form workflow looks like this:

Open Form
   ↓
Find Input
   ↓
Enter Data
   ↓
Select Values
   ↓
Handle Keyboard
   ↓
Validate Fields
   ↓
Submit
   ↓
Verify Result
Enter fullscreen mode Exit fullscreen mode

Definition

XCUITest form testing validates how iOS form controls accept user input, manage interaction states, display validation feedback, and submit data through automated UI tests.

Key Points

  • Use accessibility identifiers for stable form controls.
  • Clear existing text before entering test data.
  • Use typeText() for text input.
  • Use secure text fields for password scenarios.
  • Validate keyboard-related behavior.
  • Handle pickers according to their exposed UI hierarchy.
  • Test required and optional fields separately.
  • Validate inline error messages.
  • Test invalid and boundary inputs.
  • Verify submit-button state.
  • Scroll to controls that are outside the visible viewport.
  • Validate the final application state after submission.
  • Avoid coordinate-based form interactions.

Why XCUITest Form Testing Matters

Forms combine multiple interaction types in one workflow.

For example:

Registration Form
      ↓
Name
      ↓
Email
      ↓
Password
      ↓
Country Picker
      ↓
Date Picker
      ↓
Terms Switch
      ↓
Keyboard
      ↓
Submit
Enter fullscreen mode Exit fullscreen mode

A test that only checks whether the Submit button works does not provide enough coverage.

A production-grade test should verify the complete interaction contract.

1. Finding Text Fields

Suppose the application exposes:

registration.firstName
registration.lastName
registration.email
Enter fullscreen mode Exit fullscreen mode

The test can locate them directly:

let firstName =
    app.textFields[
        "registration.firstName"
    ]

let email =
    app.textFields[
        "registration.email"
    ]
Enter fullscreen mode Exit fullscreen mode

Then verify availability:

XCTAssertTrue(
    firstName.waitForExistence(
        timeout: 10
    )
)

XCTAssertTrue(
    email.waitForExistence(
        timeout: 10
    )
)
Enter fullscreen mode Exit fullscreen mode

Stable identifiers make form tests less dependent on visible text.

2. Entering Text With typeText()

The standard interaction is:

email.tap()

email.typeText(
    "qa@example.com"
)
Enter fullscreen mode Exit fullscreen mode

A complete example:

let email =
    app.textFields[
        "registration.email"
    ]

XCTAssertTrue(
    email.waitForExistence(
        timeout: 10
    )
)

email.tap()

email.typeText(
    "qa@example.com"
)
Enter fullscreen mode Exit fullscreen mode

The test should interact with the field rather than attempting to manipulate the application through coordinates.

3. Clearing Existing Text

If a field already contains data, clear it before entering the expected value.

One common approach is:

let email =
    app.textFields[
        "registration.email"
    ]

email.tap()

email.press(
    forDuration: 1.0
)
Enter fullscreen mode Exit fullscreen mode

However, selection behavior can vary.

A reusable helper can make clearing behavior consistent:

extension XCUIElement {

    func clearText() {

        guard let value =
            value as? String else {
            return
        }

        tap()

        let deleteCount =
            value.count

        for _ in 0..<deleteCount {
            typeText("\u{8}")
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Then:

email.clearText()

email.typeText(
    "new@example.com"
)
Enter fullscreen mode Exit fullscreen mode

For production frameworks, keep text-clearing behavior centralized rather than duplicating it across tests.

4. Testing Secure Text Fields

Passwords should normally be exposed as secure text fields.

let password =
    app.secureTextFields[
        "registration.password"
    ]

XCTAssertTrue(
    password.waitForExistence(
        timeout: 10
    )
)

password.tap()

password.typeText(
    "StrongPassword123!"
)
Enter fullscreen mode Exit fullscreen mode

The test can then verify the field exists without asserting the actual password value is visibly displayed.

5. Testing Text Field Validation

Invalid input should be tested deliberately.

let email =
    app.textFields[
        "registration.email"
    ]

email.tap()

email.typeText(
    "invalid-email"
)

app.buttons[
    "registration.submit"
].tap()
Enter fullscreen mode Exit fullscreen mode

Then verify the validation message:

let error =
    app.staticTexts[
        "registration.email.error"
    ]

XCTAssertTrue(
    error.waitForExistence(
        timeout: 5
    )
)
Enter fullscreen mode Exit fullscreen mode

A strong test verifies:

Invalid Input
     ↓
Submit
     ↓
Validation Triggered
     ↓
Error Displayed
     ↓
Form Remains Available
Enter fullscreen mode Exit fullscreen mode

6. Testing Required Fields

A required-field scenario should verify that submission does not proceed without mandatory data.


👉 Continue reading the full article on skakarh.com →

Originally published at skakarh.com/xcuitest-form-testing.
Subscribe to QA Pulse by SK
weekly signal for QA, Test Automation and AI in Software Engineering.

Top comments (0)