SwiftUI中使用swift-snapshot-testing如何等待异步Task完成?
解决SwiftUI快照测试异步加载问题
问题1:异步配置Mock的替代实现方式
不需要额外创建Task来配置异步Mock,直接把测试方法改成异步函数即可。XCTest从Xcode 13开始支持异步测试方法,你可以直接在方法内同步配置异步Mock:
func testPrintersListViewSnapShot() async { let mockedPrinterResult: [PrinterModel] = [ PrinterModel.moc(status: .maintenance, id: 1) ] // 直接在异步测试方法内配置Mock,无需额外Task given(await printerControllerMock.fetchPrinters()).willReturn(.success(mockedPrinterResult)) let printersViewModel = PrintersViewModel(printerController: printerControllerMock, errorHandler: errorHandlerMock) let sut = PrintersListView(printersViewModel: printersViewModel) // 后续处理异步等待逻辑... assertSnapshot(of: sut, as: .image) }
这样既避免了额外的Task调度,也让Mock配置逻辑更清晰。
问题2:等待异步任务完成后生成快照
要解决快照提前生成的问题,核心是等待ViewModel的异步数据加载完成,再触发快照。这里提供两种可靠方案:
方案1:利用ViewModel的状态监听(推荐)
如果你的PrintersViewModel有类似@Published var isLoading: Bool或者@Published var printers: [PrinterModel]?的状态属性,可以通过监听这些属性的变化,等待数据加载完成:
func testPrintersListViewSnapShot() async { let mockedPrinterResult: [PrinterModel] = [ PrinterModel.moc(status: .maintenance, id: 1) ] given(await printerControllerMock.fetchPrinters()).willReturn(.success(mockedPrinterResult)) let printersViewModel = PrintersViewModel(printerController: printerControllerMock, errorHandler: errorHandlerMock) let sut = PrintersListView(printersViewModel: printersViewModel) // 等待ViewModel的打印机数据加载完成 await printersViewModel.$printers.values.first { $0 != nil } assertSnapshot(of: sut, as: .image) }
注:需要确保ViewModel的@Published属性在数据加载完成时正确触发更新。
方案2:手动触发数据加载(跳过.task自动执行)
如果PrintersViewModel对外暴露了数据加载的方法(比如func fetchPrinters()),可以直接在测试里手动调用并等待完成,不用依赖视图的.task修饰符,这样更可控:
func testPrintersListViewSnapShot() async { let mockedPrinterResult: [PrinterModel] = [ PrinterModel.moc(status: .maintenance, id: 1) ] given(await printerControllerMock.fetchPrinters()).willReturn(.success(mockedPrinterResult)) let printersViewModel = PrintersViewModel(printerController: printerControllerMock, errorHandler: errorHandlerMock) // 手动触发并等待数据加载完成 await printersViewModel.fetchPrinters() let sut = PrintersListView(printersViewModel: printersViewModel) assertSnapshot(of: sut, as: .image) }
这种方式完全绕过了视图的异步生命周期,直接控制数据加载时机,测试稳定性更高。
备选方案:利用延迟等待视图更新
如果以上方案不适用,还可以用XCTest的期望等待视图完成重渲染(仅作为备选,稳定性略差):
func testPrintersListViewSnapShot() async { let mockedPrinterResult: [PrinterModel] = [ PrinterModel.moc(status: .maintenance, id: 1) ] given(await printerControllerMock.fetchPrinters()).willReturn(.success(mockedPrinterResult)) let printersViewModel = PrintersViewModel(printerController: printerControllerMock, errorHandler: errorHandlerMock) let sut = PrintersListView(printersViewModel: printersViewModel) // 创建期望,等待视图完成重渲染 let expectation = XCTestExpectation(description: "View updated with data") DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { expectation.fulfill() } await fulfillment(of: [expectation], timeout: 1) assertSnapshot(of: sut, as: .image) }
内容的提问来源于stack exchange,提问作者esbenr
相关产品推荐
相关产品推荐

