单元测试组合

问题描述 投票:0回答:3

我在测试组合时遇到困难。我正在关注:

哪些测试:

final class ViewModel {
    @Published private(set) var tokens = [String]()
    @Published var string = ""
 
    private let tokenizer = Tokenizer()

    init () {
        $string
            .flatMap(tokenizer.tokenize)
            .replaceError(with: [])
            .assign(to: &$tokens)
    }
}

struct Tokenizer {
    func tokenize(_ string: String) -> AnyPublisher<[String], Error> {
        let strs = string.components(separatedBy: " ")
        return Just(strs)
            .setFailureType(to: Error.self)
            .eraseToAnyPublisher()
    }
}

具有以下内容:

func testTokenizingMultipleStrings() throws {
    let viewModel = ViewModel()
    let tokenPublisher = viewModel.$tokens
        .dropFirst()
        .collect(2)
        .first()
    viewModel.string = "Hello @john"
    viewModel.string = "Check out #swift"
    let tokenArrays = try awaitPublisher(tokenPublisher)
    XCTAssertEqual(tokenArrays.count, 2)
    XCTAssertEqual(tokenArrays.first, ["Hello", "john"])
    XCTAssertEqual(tokenArrays.last, ["Check out", "swift"])
}

以及以下辅助函数:

extension XCTestCase {
    func awaitPublisher<T: Publisher>(
        _ publisher: T,
        timeout: TimeInterval = 10,
        file: StaticString = #file,
        line: UInt = #line
    ) throws -> T.Output {
        var result: Result<T.Output, Error>?
        let expectation = self.expectation(description: "Awaiting publisher")
        let cancellable = publisher.sink(
            receiveCompletion: { completion in
                switch completion {
                case .failure(let error):
                    result = .failure(error)
                case .finished:
                    break
                }
                expectation.fulfill()
            },
            receiveValue: { value in
                result = .success(value)
            }
        )
        waitForExpectations(timeout: timeout)
        cancellable.cancel()
        let unwrappedResult = try XCTUnwrap(
            result,
            "Awaited publisher did not produce any output",
            file: file,
            line: line
        )
        return try unwrappedResult.get()
    }
}

此处

receiveValue
从未被调用,因此测试未完成。 我怎样才能通过这个测试?

swift xctest combine
3个回答
2
投票

我遇到了同样的问题,最终意识到为了通过测试,我们需要在设置订阅和等待期望之间更新视图模型。由于这两种情况当前都发生在 awaitPublisher 帮助器内部,因此我向该函数添加了一个闭包参数:

func awaitPublisher<T: Publisher>(
    _ publisher: T,
    timeout: TimeInterval = 10,
    file: StaticString = #file,
    line: UInt = #line,
    closure: () -> Void
) throws -> T.Output {
    ...
    let expectation = ...
    let cancellation = ...

    closure()

    waitForExpectations(timeout: timeout)
    ...
}

注意闭包的确切位置——调用太早或太晚都不会起作用。

然后您可以在测试中调用助手,如下所示:

let tokenArrays = try awaitPublisher(publisher) { viewModel.string = "Hello @john" viewModel.string = "Check out #swift" }



0
投票
您的 
tokenPublisher

在您订阅之前不会执行任何操作。在此代码中,您创建发布者,执行一些操作,

would
已通过发布者推送值if有人订阅了它,然后您调用awaitPublisher(执行订阅的事情)。你需要扭转这些:
    let viewModel = ViewModel()
    let tokenPublisher = viewModel.$tokens
        .dropFirst()
        .collect(2)
        .first()
    let tokenArrays = try awaitPublisher(tokenPublisher)
    viewModel.string = "Hello @john"
    viewModel.string = "Check out #swift"



0
投票
Swift Package

,它优雅地解决了我们的痛点。 这位作者救了我一天。 这是我的示例测试函数。

import Combine import TestableCombinePublishers import XCTest func testCollect3Values() { let values = [0, 1, 2] let intValue = CurrentValueSubject<Int, Never>(-1) intValue.send(values[0]) let test = intValue .collect(values.count) // 3 .expect(values) // [0, 1, 2] .expectNoCompletion() intValue.send(values[1]) intValue.send(values[2]) test.waitForExpectations(timeout: 1) }

© www.soinside.com 2019 - 2024. All rights reserved.