Swift
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Optionals
-
Section 4: Object-Oriented and Value Types
-
Section 5: Memory Management
-
Section 6: Generics and Error Handling
-
Section 7: Concurrency
-
Section 8: Working with Collections
-
Section 9: Codable and Data Handling
-
Section 10: Protocol-Oriented Programming
-
Section 11: Testing and Tooling
-
Section 12: Practical Projects
-
Section 13: Interview Practice
-
Section 14: More Practice Exercises
-
Section 15: More Standard Library
-
Section 16: Advanced Concurrency
-
Section 17: Foundation Framework Deep Dive
-
Section 18: URLSession and Networking Deep Dive
-
Section 19: Combine Framework
-
Section 20: SwiftUI Fundamentals for Swift Developers
-
Section 21: Server-Side Swift with Vapor
-
Section 22: Swift Package Manager Deep Dive
-
Section 23: Swift Concurrency Deep Dive
-
Section 24: More Language Features
-
Section 25: Error Handling Deep Dive
-
Section 26: Testing Deep Dive
-
Section 27: Data Structures and Algorithms in Swift
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Swift Macros (Swift 5.9+)
-
Section 31: Property Wrappers Ecosystem
-
Section 32: Swift Interop Deep Dive
-
Section 33: iOS App Architecture Patterns
-
Section 34: Performance and Debugging
-
Section 35: App Distribution and CI/CD
-
Section 36: More Practical Projects
-
Section 37: SwiftData and Persistence
-
Section 38: More Design Patterns
-
Section 39: More Review and Practice
-
Section 40: More Foundation Deep Dive
-
Section 41: Advanced Collections in Swift
-
Section 42: Advanced Generics Practice
-
Section 43: UIKit for Legacy and Hybrid Apps
-
Section 44: watchOS and visionOS Development Basics
-
Section 45: More Networking Patterns
-
Section 46: More Testing Practice
-
Section 47: Accessibility in Swift Apps
-
Section 48: Localization
-
Section 49: More Practical Projects Round 2
-
Section 50: Swift Charts Framework
-
Section 51: More Interview and Algorithm Practice
-
Section 52: Final Practice and Mastery
-
Section 53: Swift Compiler and Build System
-
Section 54: More Concurrency Practice
-
Section 55: App Store Guidelines and Review
-
Section 56: More Design and Architecture
48: UI Testing Basics
Imagine you've just hired a professional "mystery shopper" to test your new retail store. You don't give them the blueprints of the building or the employee handbook. Instead, you tell them: "Walk through the front door, find the shoes section, pick up a pair of sneakers, and try to pay for them at the register." The shopper doesn't care how the inventory database works or how the credit card API is written; they only care that the door opens, the shoes are on the shelf, and the register actually takes their money.
That is exactly what UI Testing is in Swift. While unit tests look at the internal plumbing (the "blueprints"), UI tests treat your app as a black box. We use the XCUITest framework to simulate a "robot user" who interacts with the screen just like a human would.
Teaching the Robot to See
Before our robot user can tap a button, it needs to find it. In the world of XCUITest, we start with an instance of XCUIApplication. Think of this as the robot's eyes. To find a specific element, we use "queries."
Let's say we're testing a simple "Task Manager" app. We want to find a text field where the user types a task and a "Add" button. Here is how we map the analogy to the code:
let app = XCUIApplication()
app.launch()
// The robot looks for a text field labeled "Task Name"
let taskField = app.textFields["Task Name"]
// The robot looks for a button labeled "Add"
let addButton = app.buttons["Add"]
I'll give you a tip here: accessibility identifiers are your best friend. Relying on the actual text (like "Add") is risky because if you change the button to say "Submit" for a rebranding, your tests will break. If you set an accessibilityIdentifier in your source code, the robot can find the button regardless of what the label says.
Simulating Human Touch
Once the robot has found an element, it needs to do something with it. The XCUITest framework provides a set of methods that mimic human interaction. The most common are .tap(), .typeText(""), and .swipeUp().
Continuing with our Task Manager example, we want to simulate the entire process of adding a task. It looks like this:
let app = XCUIApplication()
app.launch()
let taskField = app.textFields["Task Name"]
taskField.tap() // Focus the field
taskField.typeText("Buy almond milk")
let addButton = app.buttons["Add"]
addButton.tap()
Notice how we have to .tap() the text field before we .typeText()? This is because the robot is literal. A human doesn't magically start typing into a field without clicking it first, and neither does the UI test runner.
Verifying the Result
Tapping buttons is useless if we aren't checking the outcome. In unit tests, you use XCTAssert to check values. In UI tests, you use it to check if an element exists or if its label changed.
If our Task Manager works, the text "Buy almond milk" should now appear in a list on the screen. We verify this by querying for a static text element with that exact value:
let resultText = app.staticTexts["Buy almond milk"]
XCTAssertTrue(resultText.exists, "The task should appear in the list after clicking Add.")
I'll be honest with you: UI tests are slower than unit tests and can occasionally be "flaky" (meaning they fail even when the code is correct, perhaps because an animation didn't finish in time). Because of this, you shouldn't test every single edge case here. Save the heavy lifting for your unit tests, and use UI tests for the "Happy Path"βthe critical journeys that must work for the user to be successful.
π Practical Task
The Login Flow Validation Test
You are tasked with writing a UI test for a Login screen. The screen contains two text fields (one for "Username" and one for "Password") and a button labeled "Login". When the login is successful, the app navigates to a dashboard that displays a label saying "Welcome Back!".
Create a test function that performs the following sequence:
- Launches the app using
XCUIApplication(). - Finds the username field and types "swift_dev".
- Finds the password field and types "password123".
- Taps the "Login" button.
- Asserts that a static text element with the value "Welcome Back!" now exists on the screen.
Note: Assume the accessibilityIdentifier for the fields matches their labels ("Username" and "Password").
There are no comments for now.