用于获取 PassthroughSubject 发布者值的单元测试

use*_*723 2 swift combine

有没有办法在单元测试中获取PassthroughSubject发布者的值?我想测试一个函数是否返回成功,并测试这个函数,我想看看何时发布者值为.loaded,然后就是成功。

class HomeViewModel: ObservableObject {

    var homeState = PassthroughSubject<StatePublisher, Never>()

    func load(item: HomeModel) {
        self.homeState.send(.loading)
        self.dataSource.load(item: item) { result in
            switch result {
            case .success:
                self.homeState.send(.loaded)
            case let .failure(error):
                self.homeState.send(.error(message: error.localizedDescription))
            }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)
class HomeViewModelTests: XCTestCase {
    var sut: ViewModel!
    var subscriptions = Set<AnyCancellable>()
    
    override func setUpWithError() throws {
        sut = ViewModel()
    }

    override func tearDownWithError() throws {
        sut = nil
        subscriptions = []
    }
        
    func testUpdateHomeSuccess() {
        let expected = StatePublisher.loaded
        var result = StatePublisher.loading
    
        sut.load(item: HomeModel.fixture())
        
        sut.homeState
            .sink(receiveValue: { state in
                result = state
            })
            .store(in: &subscriptions)
    
        XCTAssert(
            result == expected,
            "Home expected to be \(expected) but was \(result)"
        )
    }
    
}
Run Code Online (Sandbox Code Playgroud)

我尝试过这样的测试,但从未调用过接收器。

rob*_*off 5

据推测,dataSource.load异步操作。这意味着您需要XCTestExpectation在测试用例中使用 an 。此外,由于您使用的是 aPassthroughSubject而不是 a ,因此您应该在开始加载之前CurrentValueSubject创建订阅,以确保您不会错过任何已发布的值。

let ex = XCTestExpectation()
sut.homeState
    .sink {
        if case .loaded = $0 {
            ex.fulfill()
        }
    }
    .store(in: &subscriptions)

wait(for: [ex], timeout: 10)
// Test fails if it doesn't call `ex.fulfill()` within 10 seconds.
Run Code Online (Sandbox Code Playgroud)

阅读 Apple 的测试异步操作与预期指南。