swift 单元测试合并

rks48beu  于 2024-01-05  发布在  Swift
关注(0)|答案(3)|浏览(221)

我在测试合并时遇到困难。我正在跟踪:

哪些测试:

  1. final class ViewModel {
  2. @Published private(set) var tokens = [String]()
  3. @Published var string = ""
  4. private let tokenizer = Tokenizer()
  5. init () {
  6. $string
  7. .flatMap(tokenizer.tokenize)
  8. .replaceError(with: [])
  9. .assign(to: &$tokens)
  10. }
  11. }
  12. struct Tokenizer {
  13. func tokenize(_ string: String) -> AnyPublisher<[String], Error> {
  14. let strs = string.components(separatedBy: " ")
  15. return Just(strs)
  16. .setFailureType(to: Error.self)
  17. .eraseToAnyPublisher()
  18. }
  19. }

字符串
其中包括:

  1. func testTokenizingMultipleStrings() throws {
  2. let viewModel = ViewModel()
  3. let tokenPublisher = viewModel.$tokens
  4. .dropFirst()
  5. .collect(2)
  6. .first()
  7. viewModel.string = "Hello @john"
  8. viewModel.string = "Check out #swift"
  9. let tokenArrays = try awaitPublisher(tokenPublisher)
  10. XCTAssertEqual(tokenArrays.count, 2)
  11. XCTAssertEqual(tokenArrays.first, ["Hello", "john"])
  12. XCTAssertEqual(tokenArrays.last, ["Check out", "swift"])
  13. }


下面是helper函数:

  1. extension XCTestCase {
  2. func awaitPublisher<T: Publisher>(
  3. _ publisher: T,
  4. timeout: TimeInterval = 10,
  5. file: StaticString = #file,
  6. line: UInt = #line
  7. ) throws -> T.Output {
  8. var result: Result<T.Output, Error>?
  9. let expectation = self.expectation(description: "Awaiting publisher")
  10. let cancellable = publisher.sink(
  11. receiveCompletion: { completion in
  12. switch completion {
  13. case .failure(let error):
  14. result = .failure(error)
  15. case .finished:
  16. break
  17. }
  18. expectation.fulfill()
  19. },
  20. receiveValue: { value in
  21. result = .success(value)
  22. }
  23. )
  24. waitForExpectations(timeout: timeout)
  25. cancellable.cancel()
  26. let unwrappedResult = try XCTUnwrap(
  27. result,
  28. "Awaited publisher did not produce any output",
  29. file: file,
  30. line: line
  31. )
  32. return try unwrappedResult.get()
  33. }
  34. }


这里receiveValue从未被调用,所以测试没有完成。我如何让这个测试通过?

iqjalb3h

iqjalb3h1#

我遇到了同样的问题,最终意识到要想通过测试,我们需要在设置订阅和等待期望之间更新view-model。由于这两个过程目前都发生在awaitPublisher助手中,我给这个函数添加了一个闭包参数:

  1. func awaitPublisher<T: Publisher>(
  2. _ publisher: T,
  3. timeout: TimeInterval = 10,
  4. file: StaticString = #file,
  5. line: UInt = #line,
  6. closure: () -> Void
  7. ) throws -> T.Output {
  8. ...
  9. let expectation = ...
  10. let cancellation = ...
  11. closure()
  12. waitForExpectations(timeout: timeout)
  13. ...
  14. }

字符串
注意闭包的确切位置--如果调用得太早或太晚,它都不会工作。
然后你可以在测试中像这样调用助手:

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

展开查看全部
b4qexyjb

b4qexyjb2#

  1. let viewModel = ViewModel()
  2. let tokenPublisher = viewModel.$tokens
  3. .dropFirst()
  4. .collect(2)
  5. .first()
  6. viewModel.string = "Hello @john"
  7. viewModel.string = "Check out #swift"
  8. let tokenArrays = try awaitPublisher(tokenPublisher)

字符串
你的tokenPublisher不会做任何事情,直到你订阅它。在这段代码中,你创建了发布者,做了一些动作,* 如果 * 有人订阅了它,* 会 * 通过发布者推送值,然后你调用awaitPublisher(订阅的东西)。你需要反转这些:

  1. let viewModel = ViewModel()
  2. let tokenPublisher = viewModel.$tokens
  3. .dropFirst()
  4. .collect(2)
  5. .first()
  6. let tokenArrays = try awaitPublisher(tokenPublisher)
  7. viewModel.string = "Hello @john"
  8. viewModel.string = "Check out #swift"

展开查看全部
zf9nrax1

zf9nrax13#

不幸的是,我没有发现上面的答案有帮助。后来,我发现了这个Swift Package,它优雅地解决了我们的痛点。这个作者保存我一天。
下面是我的测试函数示例。

  1. import Combine
  2. import TestableCombinePublishers
  3. import XCTest
  4. func testCollect3Values() {
  5. let values = [0, 1, 2]
  6. let intValue = CurrentValueSubject<Int, Never>(-1)
  7. intValue.send(values[0])
  8. let test = intValue
  9. .collect(values.count) // 3
  10. .expect(values) // [0, 1, 2]
  11. .expectNoCompletion()
  12. intValue.send(values[1])
  13. intValue.send(values[2])
  14. test.waitForExpectations(timeout: 1)
  15. }

字符串

展开查看全部

相关问题