Reputation: 1044
I have a simple ViewModel
with one property:
class ViewModel {
var name = Variable<String>("")
}
And I'm binding it to its UITextField
in my ViewController
:
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)
}
}
Now, I want to Unit Test it.
I'm able to fill the nameField.text
property though Rx using .onNext
event - nameField.rx.text.onNext("My Name Here")
.
But the viewModel.name
isn't being filled. Why? How can I make this Rx behavior work in my XCTestCase?
Please see below the result of my last test run:
Upvotes: 1
Views: 1982
Reputation: 1695
rx.text relies on the following UIControlEvents: .allEditingEvents
and .valueChanged
. Thus, explicitly send a onNext events will not send action for these event. You should send action manually.
sut.nameField.rx.text.onNext(name)
sut.nameField.sendActions(for: .valueChanged)
Upvotes: 1
Reputation: 1611
I believe the issue you are having is that the binding is not explicitly scheduled on the main thread. I recommend using a Driver
in this case:
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)
}
// ..
With a Driver
, the binding will always happen on the main thread and it will not error out.
Also, in your test I would call skip
on the binding:
sut.nameTextField.rx.text.skip(1).onNext(name)
because the Driver
will replay the first element when it subscribes.
Finally, I suggest using RxTest and a TestScheduler
instead of GCD.
Let me know if this helps.
Upvotes: 2