3.9 KiB
3.9 KiB
name, description, compatibility, metadata
| name | description | compatibility | metadata | ||||
|---|---|---|---|---|---|---|---|
| swift-testing-patterns | Unit testing, Mocking, UI testing patterns cho Swift Enterprise (XCTest, async testing). Use for ViewModels testing, mocking services, hoặc testing best practices. | Swift 5.9+, XCTest, Swift Testing |
|
Swift Testing Patterns
Unit và Integration testing patterns cho Swift Enterprise.
When to Use This Skill / Khi Nào Sử Dụng
Use this skill when:
- Writing unit tests / Viết unit tests
- Mocking services / Mock services
- Testing ViewModels / Test ViewModels
- Async code testing / Test async code
Core Patterns / Mẫu Chính
Mock Service
// Tests/Mocks/MockAPIService.swift
final class MockAPIService: APIServiceProtocol {
var mockResult: Any?
var mockError: Error?
var requestCalled = false
func request<T: Decodable>(
endpoint: String,
method: HTTPMethod,
body: Encodable?,
headers: [String: String]?
) async throws -> T {
requestCalled = true
if let error = mockError {
throw error
}
guard let result = mockResult as? T else {
throw APIError.unknown
}
return result
}
}
ViewModel Testing
// Tests/ViewModelTests/HomeViewModelTests.swift
import XCTest
@testable import MyApp
@MainActor
final class HomeViewModelTests: XCTestCase {
var sut: HomeViewModel!
var mockService: MockAPIService!
override func setUp() {
super.setUp()
mockService = MockAPIService()
sut = HomeViewModel(apiService: mockService)
}
override func tearDown() {
sut = nil
mockService = nil
super.tearDown()
}
func test_loadItems_success() async {
// Arrange
let expectedItems = [Item(id: "1", name: "Test")]
mockService.mockResult = expectedItems
// Act
await sut.loadItems()
// Assert
XCTAssertEqual(sut.items.count, 1)
XCTAssertEqual(sut.items.first?.name, "Test")
XCTAssertFalse(sut.isLoading)
XCTAssertNil(sut.errorMessage)
}
func test_loadItems_failure() async {
// Arrange
mockService.mockError = APIError.networkError(NSError(domain: "", code: -1))
// Act
await sut.loadItems()
// Assert
XCTAssertTrue(sut.items.isEmpty)
XCTAssertNotNil(sut.errorMessage)
}
}
Async Testing
func test_asyncOperation() async throws {
// Use async/await directly
let result = try await sut.performAsync()
XCTAssertTrue(result)
}
// With expectation (legacy)
func test_asyncWithExpectation() {
let expectation = expectation(description: "Async complete")
Task {
await sut.loadData()
expectation.fulfill()
}
wait(for: [expectation], timeout: 5.0)
XCTAssertFalse(sut.items.isEmpty)
}
Published Property Testing
import Combine
func test_publishedProperty() {
var cancellables = Set<AnyCancellable>()
let expectation = expectation(description: "Value changed")
sut.$items
.dropFirst() // Skip initial value
.sink { items in
XCTAssertEqual(items.count, 1)
expectation.fulfill()
}
.store(in: &cancellables)
Task {
await sut.loadItems()
}
wait(for: [expectation], timeout: 5.0)
}
Quick Reference / Tham Chiếu Nhanh
| Pattern | Usage |
|---|---|
| Mock Service | Protocol + mock implementation |
@MainActor |
Required for ViewModel tests |
| Arrange-Act-Assert | Standard test structure |
async throws |
Direct async testing |