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

单元测试@Published变量:解决监听时底层值未同步问题

问题描述

我有如下ViewModel代码结构:

class ViewModel: ObservableObject {
    @Published var data: [Int] = []

    func fetchData() {
        someAPICall() { result in
            data = result // 假设此处会插入10个元素
        }
    }
}

编写的单元测试代码如下:

func testViewModel() {
    let waitExpectation = expectation(description: "testVM")
    let viewModel = ViewModel()
    viewModel.$contentUnitViewModels
    .dropFirst() // 忽略VM初始化时的首次发布
    .sink { data in
        waitExpectation.fulfill()
        XCTAssertEqual(viewModel.data.count, 10)
        // viewModel.data为空,但闭包中的data有预期的10个值
    }
    .store(in: &cancellables)

    viewModel.fetchData()

    waitForExpectations(timeout: waitExpectationTimeout) { error in
        XCTAssertNil(error)
    }
}

遇到的问题:在SwiftUI视图中使用@Published属性data时数据能正常渲染,但单元测试里viewModel.data为空(count为0),而sink闭包中传入的data却有10个值,看起来发布通知比底层数组实际设置更早,想知道如何修复,确保只有在数组真正设置后才收到通知。

解决方案

1. 修正测试中的订阅对象错误

你测试代码里订阅的是viewModel.$contentUnitViewModels,但ViewModel定义的@Published属性是data,这是明显笔误。把订阅对象改成viewModel.$data,才能正确监听data的变化:

viewModel.$data
    .dropFirst()
    .sink { receivedData in
        waitExpectation.fulfill()
        // 直接断言收到的数据符合预期
        XCTAssertEqual(receivedData.count, 10)
        // 同时验证ViewModel的属性和收到的数据一致
        XCTAssertEqual(receivedData, viewModel.data)
    }
    .store(in: &cancellables)

2. 确保在主线程更新@Published属性

@Published的通知默认会在属性设置的线程发送,如果someAPICall的回调是在后台线程,直接设置data可能导致属性更新和通知发布的时序问题。修正ViewModel的fetchData方法,确保在主线程更新data:

func fetchData() {
    someAPICall() { result in
        DispatchQueue.main.async {
            self.data = result
        }
    }
}

ObservableObject的属性更新建议始终在主线程执行,这不仅能解决测试中的时序问题,也符合SwiftUI视图更新必须在主线程的要求。

3. 优化测试断言逻辑

sink闭包中收到的receivedData就是data更新后的最新值,直接断言这个值的属性即可,同时验证它和viewModel.data的一致性,确保两者状态同步。

经过以上调整,测试就能正确捕获data的更新,并且viewModel.data和闭包中收到的数据会保持一致,不会出现空值的情况。

内容的提问来源于stack exchange,提问作者Shawn Frank

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 03:10:05