Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
  • 第 10 章:测试 SwiftData 应用

第 10 章:测试 SwiftData 应用

如何为 SwiftData 相关的代码编写可测试的代码

1. 依赖注入与协议抽象

// 定义数据操作协议
protocol DataManagerProtocol {
    func fetchItems(predicate: Predicate<Item>?) throws -> [Item]
    func saveItem(_ item: Item) throws
    func deleteItem(_ item: Item) throws
}

// 实现具体类型
class SwiftDataManager: DataManagerProtocol {
    private let context: ModelContext
    
    init(context: ModelContext) {
        self.context = context
    }
    
    func fetchItems(predicate: Predicate<Item>?) throws -> [Item] {
        let descriptor = FetchDescriptor<Item>(predicate: predicate)
        return try context.fetch(descriptor)
    }
    
    // ...其他方法实现
}

2. 使用内存存储进行测试

class DataManagerTests: XCTestCase {
    private var testContainer: ModelContainer!
    private var testContext: ModelContext!
    
    override func setUp() {
        let config = ModelConfiguration(isStoredInMemoryOnly: true)
        testContainer = try! ModelContainer(for: Item.self, configurations: config)
        testContext = testContainer.mainContext
    }
    
    func testFetchEmptyItems() throws {
        let manager = SwiftDataManager(context: testContext)
        let items = try manager.fetchItems(predicate: nil)
        XCTAssertTrue(items.isEmpty)
    }
}

3. Mock 对象策略

class MockDataManager: DataManagerProtocol {
    var mockItems: [Item] = []
    var lastSavedItem: Item?
    
    func fetchItems(predicate: Predicate<Item>?) throws -> [Item] {
        return mockItems
    }
    
    func saveItem(_ item: Item) throws {
        lastSavedItem = item
    }
    
    // ...其他mock实现
}

class ViewModelTests: XCTestCase {
    func testViewModelLoadsItems() {
        let mockManager = MockDataManager()
        mockManager.mockItems = [Item(name: "Test")]
        
        let viewModel = MyViewModel(dataManager: mockManager)
        viewModel.loadItems()
        
        XCTAssertEqual(viewModel.items.count, 1)
    }
}

4. 测试谓词与查询

func testPredicateFiltering() throws {
    let manager = SwiftDataManager(context: testContext)
    
    // 添加测试数据
    let item1 = Item(name: "Important", isCompleted: false)
    let item2 = Item(name: "Trivial", isCompleted: true)
    try manager.saveItem(item1)
    try manager.saveItem(item2)
    
    // 测试谓词
    let predicate = #Predicate<Item> { $0.isCompleted == false }
    let results = try manager.fetchItems(predicate: predicate)
    
    XCTAssertEqual(results.count, 1)
    XCTAssertEqual(results.first?.name, "Important")
}

5. 异步测试策略

func testAsyncOperations() async throws {
    let manager = SwiftDataManager(context: testContext)
    let newItem = Item(name: "Async Test")
    
    // 在主线程外执行保存
    try await Task.detached {
        try manager.saveItem(newItem)
    }.value
    
    // 验证结果
    let items = try manager.fetchItems(predicate: nil)
    XCTAssertEqual(items.count, 1)
}

6. 测试错误处理

func testSaveErrorHandling() {
    let manager = SwiftDataManager(context: testContext)
    let invalidItem = Item(name: "") // 假设空名称无效
    
    XCTAssertThrowsError(try manager.saveItem(invalidItem)) { error in
        XCTAssertTrue(error is ValidationError)
    }
}

最佳实践总结

  1. 分离业务逻辑与持久层:通过协议隔离SwiftData具体实现
  2. 控制测试范围:单元测试应聚焦业务逻辑,而非持久层实现细节
  3. 使用内存存储:isStoredInMemoryOnly配置确保测试隔离性
  4. 预置测试数据:setUp方法中初始化标准测试数据集
  5. 验证上下文状态:测试后检查ModelContext的状态一致性
  6. 性能考量:大量测试数据时使用@MainActor避免UI线程阻塞
Last Updated:: 5/30/25, 5:48 PM