第 10 章:测试 SwiftData 应用
UI 测试:模拟数据操作
为什么需要 UI 测试 SwiftData 应用?
UI 测试是验证用户界面与数据层交互的关键环节,尤其对于 SwiftData 应用:
- 确保数据变化能正确反映在 UI 上
- 验证用户操作(如滑动删除、表单提交)能正确持久化数据
- 检测数据加载状态下的 UI 表现(如空数据视图)
测试环境搭建
1. 使用内存存储
// 测试用例中配置内存存储
func setUp() {
let config = ModelConfiguration(isStoredInMemoryOnly: true)
let container = try! ModelContainer(for: TodoItem.self, configurations: config)
let context = container.mainContext
}
2. 预填充测试数据
private func insertMockItems(count: Int) -> [TodoItem] {
var items = [TodoItem]()
for i in 1...count {
let item = TodoItem(title: "Test Item \(i)")
context.insert(item)
items.append(item)
}
try! context.save()
return items
}
核心测试场景
场景 1:列表数据绑定测试
func testListDisplaysItems() {
// Given
let testItems = insertMockItems(count: 3)
// When
let app = XCUIApplication()
app.launch()
// Then
XCTAssertEqual(app.tables.cells.count, 3)
XCTAssertTrue(app.staticTexts["Test Item 1"].exists)
}
场景 2:新增数据测试
func testAddNewItem() {
// Given
let app = XCUIApplication()
app.launch()
// When
app.navigationBars["Items"].buttons["Add"].tap()
app.textFields["Title"].tap()
app.textFields["Title"].typeText("New Test Item")
app.buttons["Save"].tap()
// Then
XCTAssertTrue(app.staticTexts["New Test Item"].exists)
// 验证持久化
let fetchRequest = FetchDescriptor<TodoItem>()
let items = try! context.fetch(fetchRequest)
XCTAssertEqual(items.last?.title, "New Test Item")
}
场景 3:删除操作测试
func testDeleteItem() {
// Given
_ = insertMockItems(count: 1)
let app = XCUIApplication()
app.launch()
// When
let cell = app.tables.cells.element(boundBy: 0)
cell.swipeLeft()
app.buttons["Delete"].tap()
// Then
XCTAssertEqual(app.tables.cells.count, 0)
// 验证数据库
let fetchRequest = FetchDescriptor<TodoItem>()
let items = try! context.fetch(fetchRequest)
XCTAssertTrue(items.isEmpty)
}
高级技巧
1. 测试排序与过滤
func testSortOrder() {
// Given
_ = insertMockItems(count: 3)
// When
let app = XCUIApplication()
app.launch()
app.segmentedControls["Sort"].buttons["By Date"].tap()
// Then
let firstCell = app.tables.cells.element(boundBy: 0)
XCTAssertTrue(firstCell.staticTexts["Test Item 3"].exists)
}
2. 测试关系更新
func testCategoryAssignment() {
// Given
let category = Category(name: "Work")
let item = TodoItem(title: "Meeting")
context.insert(category)
context.insert(item)
// When
let app = XCUIApplication()
app.launch()
app.tables.cells["Meeting"].tap()
app.pickers["categoryPicker"].selectValue("Work")
app.buttons["Save"].tap()
// Then
XCTAssertEqual(item.category?.name, "Work")
}
最佳实践
- 隔离测试数据:每个测试用例使用独立的 ModelContainer
- 清理资源:在
tearDown()中重置上下文
override func tearDown() {
try? context.delete(model: TodoItem.self)
context = nil
container = nil
}
- 组合测试:将 XCTest 与 SwiftUI 预览结合使用
- 性能考量:使用
measure块测试大数据量下的 UI 响应
常见问题处理
// 处理异步更新
let expectation = XCTestExpectation(description: "Wait for UI update")
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
expectation.fulfill()
}
wait(for: [expectation], timeout: 2)
通过系统化的 UI 测试,可以确保 SwiftData 应用在真实用户操作场景下的稳定性和数据一致性。
