@Published 未在测试中发布更新
@Published not emitting update in tests
所以我遇到了这个问题,@Published
领域结果在测试中没有更新。
基本上我想做的是测试领域更新是否导致我的 ViewModel 更新 ui。为此,我订阅了已发布的字段。
这是测试:
func testListUpdate() {
// when
let expectation = XCTestExpectation(description: "Wasn't updated.")
_ = sut.$stocks
.dropFirst()
.sink { value in
expectation.fulfill()
}
try! realm.write {
realm.add(Stock(value: ["name": "Test3"]))
}
// then
wait(for: [expectation], timeout: 1)
}
sut
是如下所示的 ViewModel:
import Foundation
import RealmSwift
class StockListViewModel: ViewModelBase, ObservableObject {
let stockRepository: StockRepository
@Published var stocks: Results<Stock>
init(stockRepository: StockRepository) {
self.stockRepository = stockRepository
stocks = stockRepository.all
.sorted(byKeyPath: "createdAt", ascending: true)
super.init(title: "StockList_title".localize())
self.notificationTokens.append(
stocks
.observe({ change in
switch change {
case .update(let updated, _, _, _):
self.stocks = updated
default: break
}
})
)
}
}
如您所见,视图模型具有 `@Published var stocks: Results.
在初始化程序中设置了库存(此更新由 Publisher
成功发出并在另一种情况下进行了测试。
然后我为这些股票分配了一个领域更改侦听器,当它更新时,我将这些股票设置为新的(参见 observe
闭包)。确实调用了此更新,但测试中的 sink
从未发出更新。因此测试失败。
所以我的问题是:为什么 Publisher
发出初始值,但从来没有发出更新值?
问题是 Publisher
的订阅没有保存。
所以行
_ = sut.$stocks
应该是
cancellable = sut.$stocks
其中 cancellable
是测试类的一个字段。
使用 Combine 时,您始终需要保留对代表订阅的 AnyCancellable
的引用 - 在您的测试用例中,return 由 sink
调用编辑,因为订阅链仅在 AnyCancellable
保存在内存中时保持活动状态。 Publisher
s 只有在有订阅者时才会发出值 - 所以如果你放弃订阅,你的 $stocks
Publisher
将没有订阅者,因此它不会发出任何值。
您通过丢弃 sink
中的 return 值来丢弃订阅。
_ = sut.$stocks
.dropFirst()
.sink
相反,您需要将 return 值存储在测试 class 上作为实例 属性。
final class YourTestClass: XCTestCase {
var cancellable: AnyCancellable?
func testListUpdate() {
// when
let expectation = XCTestExpectation(description: "Wasn't updated.")
cancellable = sut.$stocks
.dropFirst()
.sink { value in
expectation.fulfill()
}
try! realm.write {
realm.add(Stock(value: ["name": "Test3"]))
}
// then
wait(for: [expectation], timeout: 1)
}
}
所以我遇到了这个问题,@Published
领域结果在测试中没有更新。
基本上我想做的是测试领域更新是否导致我的 ViewModel 更新 ui。为此,我订阅了已发布的字段。
这是测试:
func testListUpdate() {
// when
let expectation = XCTestExpectation(description: "Wasn't updated.")
_ = sut.$stocks
.dropFirst()
.sink { value in
expectation.fulfill()
}
try! realm.write {
realm.add(Stock(value: ["name": "Test3"]))
}
// then
wait(for: [expectation], timeout: 1)
}
sut
是如下所示的 ViewModel:
import Foundation
import RealmSwift
class StockListViewModel: ViewModelBase, ObservableObject {
let stockRepository: StockRepository
@Published var stocks: Results<Stock>
init(stockRepository: StockRepository) {
self.stockRepository = stockRepository
stocks = stockRepository.all
.sorted(byKeyPath: "createdAt", ascending: true)
super.init(title: "StockList_title".localize())
self.notificationTokens.append(
stocks
.observe({ change in
switch change {
case .update(let updated, _, _, _):
self.stocks = updated
default: break
}
})
)
}
}
如您所见,视图模型具有 `@Published var stocks: Results.
在初始化程序中设置了库存(此更新由 Publisher
成功发出并在另一种情况下进行了测试。
然后我为这些股票分配了一个领域更改侦听器,当它更新时,我将这些股票设置为新的(参见 observe
闭包)。确实调用了此更新,但测试中的 sink
从未发出更新。因此测试失败。
所以我的问题是:为什么 Publisher
发出初始值,但从来没有发出更新值?
问题是 Publisher
的订阅没有保存。
所以行
_ = sut.$stocks
应该是
cancellable = sut.$stocks
其中 cancellable
是测试类的一个字段。
使用 Combine 时,您始终需要保留对代表订阅的 AnyCancellable
的引用 - 在您的测试用例中,return 由 sink
调用编辑,因为订阅链仅在 AnyCancellable
保存在内存中时保持活动状态。 Publisher
s 只有在有订阅者时才会发出值 - 所以如果你放弃订阅,你的 $stocks
Publisher
将没有订阅者,因此它不会发出任何值。
您通过丢弃 sink
中的 return 值来丢弃订阅。
_ = sut.$stocks
.dropFirst()
.sink
相反,您需要将 return 值存储在测试 class 上作为实例 属性。
final class YourTestClass: XCTestCase {
var cancellable: AnyCancellable?
func testListUpdate() {
// when
let expectation = XCTestExpectation(description: "Wasn't updated.")
cancellable = sut.$stocks
.dropFirst()
.sink { value in
expectation.fulfill()
}
try! realm.write {
realm.add(Stock(value: ["name": "Test3"]))
}
// then
wait(for: [expectation], timeout: 1)
}
}