【发布时间】:2018-02-18 20:00:07
【问题描述】:
我对 RxSwift 很陌生。我有一个具有预先输入/自动完成功能的视图控制器(即,用户在 UITextField 中输入,一旦他们输入至少 2 个字符,就会发出网络请求以搜索匹配的建议)。控制器的viewDidLoad调用如下方法来设置一个Observable:
class TypeaheadResultsViewController: UIViewController {
var searchTextFieldObservable: Observable<String>!
@IBOutlet weak var searchTextField: UITextField!
private let disposeBag = DisposeBag()
var results: [TypeaheadResult]?
override func viewDidLoad() {
super.viewDidLoad()
//... unrelated setup stuff ...
setupSearchTextObserver()
}
func setupSearchTextObserver() {
searchTextFieldObservable =
self.searchTextField
.rx
.text
.throttle(0.5, scheduler: MainScheduler.instance)
.map { $0 ?? "" }
searchTextFieldObservable
.filter { $0.count >= 2 }
.flatMapLatest { searchTerm in self.search(for: searchTerm) }
.subscribe(
onNext: { [weak self] searchResults in
self?.resetResults(results: searchResults)
},
onError: { [weak self] error in
print(error)
self?.activityIndicator.stopAnimating()
}
)
.disposed(by: disposeBag)
// This is the part I want to test:
searchTextFieldObservable
.filter { $0.count < 2 }
.subscribe(
onNext: { [weak self] _ in
self?.results = nil
}
)
.disposed(by: disposeBag)
}
}
这似乎工作正常,但我正在努力弄清楚如何单元测试searchTextFieldObservable 的行为。
为简单起见,我只需要一个单元测试来验证 results 是否设置为 nil,而 searchTextField 在更改事件后的字符数少于 2 个。
我尝试了几种不同的方法。我的测试目前看起来像这样:
class TypeaheadResultsViewControllerTests: XCTestCase {
var ctrl: TypeaheadResultsViewController!
override func setUp() {
super.setUp()
let storyboard = UIStoryboard(name: "MainStoryboard", bundle: nil)
ctrl = storyboard.instantiateViewController(withIdentifier: "TypeaheadResultsViewController") as! TypeaheadResultsViewController
}
override func tearDown() {
ctrl = nil
super.tearDown()
}
/// Verify that the searchTextObserver sets the results array
/// to nil when there are less than two characters in the searchTextView
func testManualChange() {
// Given: The view is loaded (this triggers viewDidLoad)
XCTAssertNotNil(ctrl.view)
XCTAssertNotNil(ctrl.searchTextField)
XCTAssertNotNil(ctrl.searchTextFieldObservable)
// And: results is not empty
ctrl.results = [ TypeaheadResult(value: "Something") ]
let tfObservable = ctrl.searchTextField.rx.text.subscribeOn(MainScheduler.instance)
//ctrl.searchTextField.rx.text.onNext("e")
ctrl.searchTextField.insertText("e")
//ctrl.searchTextField.text = "e"
do {
guard let result =
try tfObservable.toBlocking(timeout: 5.0).first() else {
return }
XCTAssertEqual(result, "e") // passes
XCTAssertNil(ctrl.results) // fails
} catch {
print(error)
}
}
基本上,我想知道如何手动/以编程方式在searchTextFieldObservable(或者,最好是在searchTextField)上触发一个事件,以触发第二个订阅中标记为“这是我要测试的部分”的代码:"。
【问题讨论】:
-
如果你删除
searchTextFieldObservable中的throttle操作符,你的测试是否成功? -
@kiwisip - 否 - 结果相同。似乎该事件从未被触发。也就是说,我在
onNext处理程序的第一行设置了一个断点,它永远不会被命中(除了第一次加载视图时)。 -
你应该使用 RxBlocking 来测试这个异步任务。测试写得不太好...您应该看到gitbook.com/book/orta/pragmatic-ios-testing/details 以编写更好的测试。如果您对此进行调试,您会看到内部某处的 onNext 被异步调用,并且当 XCTAssert 已经完成时仍可以继续运行。
-
我已经更新了测试代码以显示现在的实际测试代码,而不是伪代码
标签: ios swift unit-testing rx-swift