考虑这个基于 XCTest 的单元测试:
func testImageRetrieved() {
let expectation = XCTestExpectation()
let cancellable = viewModel.$image.dropFirst().sink { _ in
// Do nothing on completion.
} receiveValue: {
XCTAssertNotNil($0)
expectation.fulfill()
}
wait(for: [expectation], timeout: 1.0)
cancellable.cancel()
}
根据 Apple 的从 XCTest 迁移测试,这应该可以直接转换成这种基于 Swift 测试的方法:
@Test
func imageRetrieved() async {
var cancellable: AnyCancellable?
await confirmation { imageRetrieved in
cancellable = viewModel.$image.dropFirst().sink { _ in
// Do nothing on completion.
} receiveValue: {
#expect($0 != nil)
imageRetrieved()
}
}
cancellable?.cancel()
}
但是,后一个测试失败,错误提示:“确认已确认 0 次,但预计确认 1 次。” 看起来确认不会“等到”发布者发出值。
在 Swift Testing 中测试 Combine 发布者的正确方法是什么?
正确。文档上说,
在这种情况下,闭包几乎立即返回,因为它所做的只是分配给一个变量。测试将看不到任何异步发布的值。
将其与迁移指南中的示例进行比较。
在闭包的末尾,
await Customer().buy(.soup)
被调用,这大概就是将触发的FoodTruck.shared.eventHandler
。对于发布者,您可以轻松将其
values
作为来获取AsyncSequence
,这更容易使用。例如,要检查发布者是否发布了至少一个元素,并且第一个元素不为零。您可以执行以下操作:timeout
还可以考虑在之前添加一个.values
。或者,您可以通过调用 来手动等待
Task.sleep
。以下测试publisher
在 2 秒内发布一个不为 nil 的元素。当然,这会使测试运行至少2 秒,有时这可能是不可取的。
你是对的,“
confirmation
不会‘等到’发布者发出值。”正如该文件所述:如果您查看
confirmation
该文档中的示例,它们在返回之前都会进行await
一些async
调用。但是您可以使用
values
来呈现您的Publisher
,AsyncSequence
您可以await
在async
测试中简单地使用它。因此,而不是sink
,只需从此异步序列中获取第一个值:因此,就您而言,您应该能够执行以下操作:
使用此扩展来简化调用
first(where:)
: