You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 07:44:51