From everything-claude-code-mobile
Provides XCTest patterns for unit tests, UI tests, mocks, async/await testing, and property testing in Swift/SwiftUI iOS apps.
npx claudepluginhub ahmed3elshaer/everything-claude-code-mobile --plugin everything-claude-code-mobileThis skill uses the workspace's default tool permissions.
Testing patterns for Swift and SwiftUI apps.
Provides guidelines, templates, and examples for writing XCTest unit tests in Swift, covering TDD practices, mocking techniques, and best practices.
Routes iOS testing queries to specialized skills for unit tests with Swift Testing, UI tests with XCUITest, flaky test debugging, async patterns, fast execution without simulator, and architecture.
Provides API reference for Apple's Swift Testing framework: @Test/@Suite functions, #expect/#require assertions, traits, parameterized tests, and XCTest migration. For iOS 18+ apps.
Share bugs, ideas, or general feedback.
Testing patterns for Swift and SwiftUI apps.
import XCTest
@testable import MyApp
class UserViewModelTests: XCTestCase {
var sut: UserViewModel!
var mockService: MockUserService!
override func setUp() {
super.setUp()
mockService = MockUserService()
sut = UserViewModel(service: mockService)
}
override func tearDown() {
sut = nil
mockService = nil
super.tearDown()
}
func testLoadUsers_whenSuccessful_populatesUsers() async throws {
// Given
let expectedUsers = [User.mock1, User.mock2]
mockService.usersToReturn = expectedUsers
// When
await sut.loadUsers()
// Then
XCTAssertEqual(sut.users, expectedUsers)
XCTAssertFalse(sut.isLoading)
XCTAssertNil(sut.errorMessage)
}
func testLoadUsers_whenFailure_setsErrorMessage() async throws {
// Given
mockService.shouldThrowError = true
// When
await sut.loadUsers()
// Then
XCTAssertTrue(sut.users.isEmpty)
XCTAssertNotNil(sut.errorMessage)
}
}
// ✅ Protocol-based mocking
protocol UserServiceProtocol {
func getUsers() async throws -> [User]
}
class MockUserService: UserServiceProtocol {
var usersToReturn: [User] = []
var shouldThrowError = false
var getUsersCalled = false
func getUsers() async throws -> [User] {
getUsersCalled = true
if shouldThrowError {
throw NetworkError.noConnection
}
return usersToReturn
}
}
// ✅ Verify method calls
func testRefreshButton_callsService() async {
// When
await sut.refresh()
// Then
XCTAssertTrue(mockService.getUsersCalled)
}
func testAsyncOperation_completesSuccessfully() async throws {
// Given
let expectation = expectation(description: "Async completes")
// When
Task {
await sut.asyncOperation()
expectation.fulfill()
}
// Then
await fulfillment(of: [expectation], timeout: 1.0)
}
// ✅ Swift async/await
func testAsyncFetch_returnsUsers() async throws {
let users = try await sut.fetchUsers()
XCTAssertFalse(users.isEmpty)
}
func testUserValidation_validEmails() {
let validEmails = [
"test@example.com",
"user.name@example.co.uk",
"user+tag@example.com"
]
for email in validEmails {
XCTAssertTrue(Validator.isValidEmail(email), "Failed for: \(email)")
}
}
func testUserValidation_invalidEmails() {
let invalidEmails = [
"invalid",
"@example.com",
"user@",
"user @example.com"
]
for email in invalidEmails {
XCTAssertFalse(Validator.isValidEmail(email), "Passed for: \(email)")
}
}
import XCTest
class LoginUITests: XCTestCase {
var app: XCUIApplication!
override func setUp() {
super.setUp()
continueAfterFailure = false
app = XCUIApplication()
app.launchArguments = ["--uitesting"]
app.launch()
}
func testLoginButton_enabledWhenFieldsFilled() {
// When
let emailField = app.textFields["emailField"]
let passwordField = app.secureTextFields["passwordField"]
let loginButton = app.buttons["loginButton"]
// Then
XCTAssertFalse(loginButton.isEnabled)
// When
emailField.tap()
emailField.typeText("test@example.com")
passwordField.tap()
passwordField.typeText("password123")
// Then
XCTAssertTrue(loginButton.isEnabled)
}
func testSuccessfulLogin_navigatesToHome() {
// Given
app.textFields["emailField"].tap()
app.textFields["emailField"].typeText("test@example.com")
app.secureTextFields["passwordField"].tap()
app.secureTextFields["passwordField"].typeText("password123")
// When
app.buttons["loginButton"].tap()
// Then
XCTAssertTrue(app.staticTexts["welcomeMessage"].exists)
}
}
// ✅ Using View snapshot testing
import SwiftUI
import XCTest
struct UserCard_Previews: PreviewProvider {
static var previews: some View {
UserCard(user: .mock)
}
}
// Snapshot test
func testUserCard_snapshot() {
let view = UserCard(user: .mock)
assertSnapshot(matching: view, as: .image(layout: .sizeThatFits))
}
import XCTest
import ViewInspector // https://github.com/nalexn/ViewInspector
class UserRowTests: XCTestCase {
func testUserRow_displaysUserName() throws {
let user = User(id: "1", name: "John Doe", email: "john@example.com")
let view = UserRow(user: user)
let text = try view.inspect().find(ViewType.Text.self).string()
XCTAssertEqual(text, "John Doe")
}
func testUserRow_tapping_callsAction() throws {
let user = User(id: "1", name: "John", email: "john@example.com")
var tapped = false
let view = UserRow(user: user, onTap: { tapped = true })
try view.inspect().find(ViewType.Button.self).tap()
XCTAssertTrue(tapped)
}
}
// ✅ Test doubles
extension User {
static let mock1 = User(id: "1", name: "John", email: "john@example.com")
static let mock2 = User(id: "2", name: "Jane", email: "jane@example.com")
}
// ✅ Wait helpers
func waitForElementToExist(
_ element: XCUIElement,
timeout: TimeInterval = 5.0
) -> Bool {
let existsPredicate = NSPredicate(format: "exists == true")
let expectation = expectation(for: existsPredicate, evaluatedWith: element)
let result = XCTWaiter.wait(for: [expectation], timeout: timeout)
return result == .completed
}
func testUserParsing_performance() {
let jsonData = largeUserJSONData
measure {
_ = try? JSONDecoder().decode([User].self, from: jsonData)
}
}
Tests/
├── Unit/
│ ├── ViewModels/
│ │ ├── HomeViewModelTests.swift
│ │ └── ProfileViewModelTests.swift
│ ├── Services/
│ │ ├── UserServiceTests.swift
│ │ └── NetworkServiceTests.swift
│ └── Models/
│ └── UserTests.swift
├── UI/
│ ├── LoginUITests.swift
│ └── HomeUITests.swift
└── Mocks/
├── MockUserService.swift
└── MockNetworkService.swift
Remember: Tests are your safety net. Write them first (TDD) or alongside your code, but always maintain them.