Files
pos-system/microservices/.agent/skills/swift-testing-patterns/SKILL.md
Ho Ngoc Hai 76d75c753b Migrate
2026-05-23 18:37:02 +07:00

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
author version
Velik Ho 1.0

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

Resources / Tài Nguyên