如何使用 RxSwift 和 XCTest 测试进入 Swift 主线程的函数?
How to test a function that gets into the main thread in Swift with RxSwift and XCTest?
我在测试我的视图时遇到了这个问题:
在我的 ViewModel 中,我调用一个异步操作,当响应到达时,我使用 PublishSubject
在我的视图中产生一个变化。在我的视图中,我调用 DispatchQueue.main.async
以隐藏或显示按钮。
视图模型
let refreshButtons = PublishSubject<Bool>(true)
refreshButtons.onNext(true)
查看
model.refreshButtons.asObservable()
.subscribe(onNext: {
[unowned self] success in
self.updateButtons(success)
})
.addDisposableTo(disposable)
private func updateButtons(_ show:Bool) {
DispatchQueue.main.async{
button.isHidden = !show
}
}
现在我不知道如何对 refreshButtons.onNext(true)
隐藏或显示我的按钮进行单元测试。
我能想到的解决办法是:
- 覆盖该方法并具有异步期望,但为此我需要制作方法 public,我不想要的,或
- 在我的 ViewModel 中而不是在视图中调度主队列,这对我来说听起来很奇怪,但我可能没问题。
我该如何解决这个问题?
提前谢谢你。
首先,你不需要测试私有方法
如果你想测试按钮是否隐藏,请尝试UI测试
这里是UI测试的WWDC。
您可以在单元测试中使用基于谓词的异步期望来等待按钮是否不再隐藏。
func testButtonIsHidden() {
// Setup your objects
let view = ...
let viewModel = ...
// Define an NSPredicate to test your expectation
let predicate = NSPredicate(block: { input, _ in
guard let _view = input as? MyView else { return false }
return _view.button.isHidden == true
})
// Create an expectation that will periodically evaluate the predicate
// to decided whether it's fulfilled or not
_ = expectation(for: predicate, evaluatedWith: view, handler: .none)
// Call the method that should generate the behaviour you are expecting.
viewModel.methodThatShouldResultInButtonBeingHidden()
// Wait for the
waitForExpectationsWithTimeout(1) { error in
if let error = error {
XCTFail("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
值得注意的是,您传递给 NSPredicate
的值应该是 class
。那是因为 类 是通过引用传递的,所以谓词块内的值将与视图模型所触及的值相同。如果您要传递 struct
或 enum
,它们是通过副本传递的,则谓词块将收到该值的副本,因为它是 运行 设置代码时的值,它总是会失败。
如果您更喜欢使用@Randall Wang 在他的回答中建议的 UI 测试,那么这个 post 可能对您有用:“How to test UI changes in Xcode 7”。 完全公开,我写了post。
我在测试我的视图时遇到了这个问题:
在我的 ViewModel 中,我调用一个异步操作,当响应到达时,我使用 PublishSubject
在我的视图中产生一个变化。在我的视图中,我调用 DispatchQueue.main.async
以隐藏或显示按钮。
视图模型
let refreshButtons = PublishSubject<Bool>(true)
refreshButtons.onNext(true)
查看
model.refreshButtons.asObservable()
.subscribe(onNext: {
[unowned self] success in
self.updateButtons(success)
})
.addDisposableTo(disposable)
private func updateButtons(_ show:Bool) {
DispatchQueue.main.async{
button.isHidden = !show
}
}
现在我不知道如何对 refreshButtons.onNext(true)
隐藏或显示我的按钮进行单元测试。
我能想到的解决办法是:
- 覆盖该方法并具有异步期望,但为此我需要制作方法 public,我不想要的,或
- 在我的 ViewModel 中而不是在视图中调度主队列,这对我来说听起来很奇怪,但我可能没问题。
我该如何解决这个问题? 提前谢谢你。
首先,你不需要测试私有方法
如果你想测试按钮是否隐藏,请尝试UI测试
这里是UI测试的WWDC。
您可以在单元测试中使用基于谓词的异步期望来等待按钮是否不再隐藏。
func testButtonIsHidden() {
// Setup your objects
let view = ...
let viewModel = ...
// Define an NSPredicate to test your expectation
let predicate = NSPredicate(block: { input, _ in
guard let _view = input as? MyView else { return false }
return _view.button.isHidden == true
})
// Create an expectation that will periodically evaluate the predicate
// to decided whether it's fulfilled or not
_ = expectation(for: predicate, evaluatedWith: view, handler: .none)
// Call the method that should generate the behaviour you are expecting.
viewModel.methodThatShouldResultInButtonBeingHidden()
// Wait for the
waitForExpectationsWithTimeout(1) { error in
if let error = error {
XCTFail("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
值得注意的是,您传递给 NSPredicate
的值应该是 class
。那是因为 类 是通过引用传递的,所以谓词块内的值将与视图模型所触及的值相同。如果您要传递 struct
或 enum
,它们是通过副本传递的,则谓词块将收到该值的副本,因为它是 运行 设置代码时的值,它总是会失败。
如果您更喜欢使用@Randall Wang 在他的回答中建议的 UI 测试,那么这个 post 可能对您有用:“How to test UI changes in Xcode 7”。 完全公开,我写了post。