XCUITest data-driven testing allows SDETs to execute the same iOS UI test scenario against multiple datasets without duplicating the test logic. Instead of creating separate tests for every username, search value, product, form input, or validation scenario, test data can be separated from the automation workflow and supplied dynamically.
This approach becomes especially useful when an XCUITest suite grows from a few scenarios into hundreds of combinations that need consistent, maintainable coverage.
What is XCUITest Data-Driven Testing?
XCUITest data-driven testing is an automation technique where test logic remains reusable while input values and expected results are supplied from external or structured datasets.
The basic architecture is:
Test Data
↓
Test Scenario
↓
Page Object
↓
XCUIElement
↓
iOS Application
↓
Expected Result
For example, instead of writing:
func testLoginWithUser1() {
// login automation
}
func testLoginWithUser2() {
// same automation
}
func testLoginWithUser3() {
// same automation
}
you can use:
let users = [
TestUser(
email: "valid@example.com",
password: "Password123",
expectedResult: .success
),
TestUser(
email: "invalid@example.com",
password: "WrongPassword",
expectedResult: .failure
)
]
The automation flow stays the same while the data changes.
Key Points
- Separate test data from test logic.
- Reuse the same test workflow.
- Model datasets with Swift structures.
- Support positive and negative scenarios.
- Use deterministic test data.
- Keep datasets readable and maintainable.
- Avoid hard-coded values throughout tests.
- Combine data-driven testing with Page Objects.
- Validate expected results per dataset.
- Keep each dataset independently identifiable.
- Avoid overly large parameter combinations.
- Generate test reports with useful dataset names.
- Use external files when datasets become large.
- Keep test data isolated between test runs.
Why Data-Driven Testing Matters in XCUITest
Traditional UI automation often starts with hard-coded values:
emailField.tap()
emailField.typeText("qa@example.com")
passwordField.tap()
passwordField.typeText("Password123")
That works for a single scenario.
But consider a registration flow requiring:
- Valid email
- Invalid email
- Empty email
- Existing email
- Invalid password
- Weak password
- Maximum-length password
- Special characters
Creating a separate test for every combination can quickly produce duplicated code.
A better approach is:
Test Workflow
│
┌────────────────┼────────────────┐
▼ ▼ ▼
Dataset 1 Dataset 2 Dataset 3
│ │ │
└────────────────┼────────────────┘
▼
Same Test Logic
This is where XCUITest data-driven testing provides significant value.
Designing a Data Model in Swift
The first step is to create a model representing one test scenario.
struct LoginTestData {
let email: String
let password: String
let expectedMessage: String
let shouldSucceed: Bool
}
Now create datasets:
let loginData = [
LoginTestData(
email: "valid@example.com",
password: "Password123",
expectedMessage: "Welcome",
shouldSucceed: true
),
LoginTestData(
email: "invalid@example.com",
password: "WrongPassword",
expectedMessage: "Invalid credentials",
shouldSucceed: false
),
LoginTestData(
email: "",
password: "Password123",
expectedMessage: "Email is required",
shouldSucceed: false
)
]
The model keeps related values together.
Instead of managing multiple arrays:
let emails = [...]
let passwords = [...]
let messages = [...]
you have one strongly typed dataset.
Running Multiple Scenarios
A simple loop can execute the same workflow for every dataset.
func testLoginScenarios() {
let scenarios = LoginTestDataProvider.all()
for scenario in scenarios {
loginPage.enterEmail(
scenario.email
)
loginPage.enterPassword(
scenario.password
)
loginPage.tapLogin()
if scenario.shouldSucceed {
XCTAssertTrue(
homePage.welcomeMessage.exists
)
} else {
XCTAssertTrue(
loginPage.errorMessage(
scenario.expectedMessage
).exists
)
}
}
}
This removes duplicated automation steps.
However, there is an important consideration.
If one iteration fails, subsequent iterations may inherit the application’s current state.
For independent scenarios, resetting the application between datasets is often safer.
Resetting Application State
A robust data-driven test should isolate scenarios.
One approach is to launch a fresh application state for each dataset:
for scenario in scenarios {
app.terminate()
app.launch()
loginPage.enterEmail(
scenario.email
)
loginPage.enterPassword(
scenario.password
)
loginPage.tapLogin()
// Validate result
}
The exact reset strategy depends on the application.
Possible approaches include:
👉 Continue reading the full article on skakarh.com →
Originally published at skakarh.com/xcuitest-data-driven-testing.
Subscribe to QA Pulse by SK —
weekly signal for QA, Test Automation and AI in Software Engineering.
Top comments (0)