iOS:含Kingfisher异步图片的UIView快照测试提前结束问题
解决异步图片加载的快照测试问题
针对Kingfisher/Nuke异步加载图片导致快照测试提前执行的问题,有以下几种不依赖占位图的解决方案:
方法1:利用异步等待机制,等待图片加载完成
在测试中使用XCTestExpectation监听图片加载的完成事件,确保图片渲染后再生成快照:
@MainActor func testSnapshot() throws { let bundle = Bundle(for: Self.self) let imageURL = try XCTUnwrap(bundle.url(forResource: "myImage", withExtension: "png")) let state = WeatherInfoView.State(temperature: "18º", text: "Overcast clouds", imageURL: imageURL, location: "Bonjal, Indonesia") let view = WeatherInfoView() view.apply(state: state) view.widthAnchor.constraint(equalToConstant: 424).isActive = true view.heightAnchor.constraint(equalToConstant: 424).isActive = true // 创建等待预期 let imageLoadedExpectation = expectation(description: "Image loaded") // 手动监听Kingfisher的图片加载完成事件 view.imageView.kf.setImage(with: imageURL) { result in switch result { case .success: imageLoadedExpectation.fulfill() case .failure(let error): XCTFail("图片加载失败:\(error)") } } // 等待预期完成,超时时间设为合理值 waitForExpectations(timeout: 2) // 图片加载完成后生成快照 assertSnapshot(of: view, as: .image) }
方法2:预加载图片到缓存,让加载变为同步
提前将本地图片加载到Kingfisher的内存缓存中,这样后续kf.setImage会直接从缓存读取,无需异步等待:
@MainActor func testSnapshot() throws { let bundle = Bundle(for: Self.self) let imageURL = try XCTUnwrap(bundle.url(forResource: "myImage", withExtension: "png")) // 预加载图片到缓存 let imageData = try Data(contentsOf: imageURL) let image = try XCTUnwrap(UIImage(data: imageData)) ImageCache.default.store(image, forKey: imageURL.absoluteString) let state = WeatherInfoView.State(temperature: "18º", text: "Overcast clouds", imageURL: imageURL, location: "Bonjal, Indonesia") let view = WeatherInfoView() view.apply(state: state) view.widthAnchor.constraint(equalToConstant: 424).isActive = true view.heightAnchor.constraint(equalToConstant: 424).isActive = true // 此时图片已在缓存,加载同步完成,可直接生成快照 assertSnapshot(of: view, as: .image) }
方法3:给视图添加测试专用的加载完成回调(不污染主代码)
通过编译条件限制,仅在测试环境下给apply方法添加完成回调,无需修改原有State结构:
首先修改视图代码:
extension WeatherInfoView: StatefulView { struct State { let temperature: String let text: String? let imageURL: URL? let location: String } #if DEBUG func apply(state: State, completion: (() -> Void)? = nil) { temperatureLabel.text = state.temperature descriptionLabel.text = state.text locationLabel.text = state.location guard let url = state.imageURL else { completion?() return } imageView.kf.setImage(with: url) { _ in completion?() } } #else func apply(state: State) { temperatureLabel.text = state.temperature descriptionLabel.text = state.text imageView.kf.setImage(with: state.imageURL) locationLabel.text = state.location } #endif }
然后在测试中使用带回调的apply方法,结合异步等待:
@MainActor func testSnapshot() throws { let bundle = Bundle(for: Self.self) let imageURL = try XCTUnwrap(bundle.url(forResource: "myImage", withExtension: "png")) let state = WeatherInfoView.State(temperature: "18º", text: "Overcast clouds", imageURL: imageURL, location: "Bonjal, Indonesia") let view = WeatherInfoView() view.widthAnchor.constraint(equalToConstant: 424).isActive = true view.heightAnchor.constraint(equalToConstant: 424).isActive = true let imageLoadedExpectation = expectation(description: "Image loaded") view.apply(state: state) { imageLoadedExpectation.fulfill() } waitForExpectations(timeout: 2) assertSnapshot(of: view, as: .image) }
内容的提问来源于stack exchange,提问作者Carlos
相关产品推荐
相关产品推荐

