如何测试 RxSwift 变量和 RxCocoa Observable 之间的 UI 绑定?
Posted
技术标签:
【中文标题】如何测试 RxSwift 变量和 RxCocoa Observable 之间的 UI 绑定?【英文标题】:How to test the UI Binding between RxSwift Variable and RxCocoa Observable? 【发布时间】:2018-02-15 17:26:54 【问题描述】:我有一个简单的ViewModel
有一个属性:
class ViewModel
var name = Variable<String>("")
我将它绑定到我的ViewController
中的UITextField
:
class ViewController : UIViewController
var viewModel : ViewModel!
@IBOutlet weak var nameField : UITextField!
private let disposeBag = DisposeBag()
override func viewDidLoad()
super.viewDidLoad()
// Binding
nameField.rx.text
.orEmpty
.bind(to: viewModel.name)
.disposed(by: disposeBag)
现在,我想对它进行单元测试。
我可以通过 Rx 使用 .onNext
事件 - nameField.rx.text.onNext("My Name Here")
填充 nameField.text
属性。
但是viewModel.name
没有被填写。为什么?如何使这种 Rx 行为在我的 XCTestCase 中起作用?
请看下面我上次测试运行的结果:
【问题讨论】:
UI 组件在尝试进行单元测试时几乎总是会出现问题,这就是它们不适合这项任务的原因。改为测试您的业务逻辑,并让 QA 进行 UI 检查。 好电话@Cristik,我会听取你的建议。我仍然很好奇为什么 Rx 没有被解雇 我推荐使用 RxTest 库和TestScheduler
而不是 GCD。然后,您可以在特定团队安排行动,并且您拥有更多控制权。
另外Variable
已被弃用。您可以使用 BehaviorRelay
和 PublishRelay
作为替代品。
【参考方案1】:
我相信您遇到的问题是绑定没有明确安排在主线程上。在这种情况下,我建议使用Driver
:
class ViewModel
var name = Variable<String>("")
class ViewController: UIViewController
let textField = UITextField()
let disposeBag = DisposeBag()
let vm = ViewModel()
override func viewDidLoad()
super.viewDidLoad()
textField.rx.text
.orEmpty
.asDriver()
.drive(vm.name)
.disposed(by: disposeBag)
// ..
使用Driver
,绑定将始终发生在主线程上,不会出错。
另外,在你的测试中,我会在绑定上调用skip
:
sut.nameTextField.rx.text.skip(1).onNext(name)
因为Driver
会在订阅时重播第一个元素。
最后,我建议使用 RxTest 和 TestScheduler
而不是 GCD。
如果这有帮助,请告诉我。
【讨论】:
【参考方案2】:rx.text 依赖于以下 UIControlEvents:.allEditingEvents
和 .valueChanged
。因此,显式发送 onNext 事件不会为这些事件发送操作。您应该手动发送操作。
sut.nameField.rx.text.onNext(name)
sut.nameField.sendActions(for: .valueChanged)
【讨论】:
以上是关于如何测试 RxSwift 变量和 RxCocoa Observable 之间的 UI 绑定?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 UITableView 的 RXswift 和 RXCocoa 中实现 tableview 单元格的内部?
Swft3 (RxSwift, RxCocoa) - TableView 使用响应式编程展开和折叠概念
在 RxCocoa/RxSwift 中,如何观察 BehaviorRelay<[object]> 数组大小发生变化