简体   繁体   English

如何使用RxSwift和XCTest测试进入Swift主线程的函数?

[英]How to test a function that gets into the main thread in Swift with RxSwift and XCTest?

I came across this problem when testing my View: 我在测试View时遇到了这个问题:

In my ViewModel I call to an asynchronous operation and when the response arrives, I use a PublishSubject to produce a change in my View. 在我的ViewModel中,我调用一个异步操作,当响应到达时,我使用PublishSubject在View中进行更改。 In my View, I call DispatchQueue.main.async in order to hide or show a button. 在我的视图中,我调用DispatchQueue.main.async以隐藏或显示按钮。

ViewModel 视图模型

let refreshButtons = PublishSubject<Bool>(true)
refreshButtons.onNext(true)

View 视图

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
   }
}

Now I don't know how to unit test that refreshButtons.onNext(true) will hide or show my button. 现在,我不知道如何对refreshButtons.onNext(true)将隐藏或显示我的按钮进行单元测试。

The solutions I can think of are: 我能想到的解决方案是:

  • Overriding the method and having an async expectation, but for that I need to make the method public, what I don't want, or 重写该方法并具有异步期望,但是为此,我需要将该方法公开,不想要的内容,或者
  • Dispatching the main queue in my ViewModel and not in the view, what it sounds odd to me, but might me ok. 在我的ViewModel中而不是视图中分派主队列,这对我来说听起来很奇怪,但也许我还可以。

How can I solve this? 我该如何解决? Thank you in advance. 先感谢您。

You could use an async expectation based on a predicate in your unit test to wait an see if the button is not hidden anymore. 您可以在单元测试中使用基于谓词的异步期望,以等待按钮不再被隐藏。

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)")
    }
  }
}

Something worth noting is that the value you pass to the NSPredicate should be a class . 值得注意的是,您传递给NSPredicate的值应该是一个class That is because classes are passed by reference, so value inside the predicate block will be the same as the one touched by your view model. 那是因为类是通过引用传递的,所以谓词块中的值将与视图模型所触及的值相同。 If you were to pass a struct or enum though, which are passed by copy, the predicate block would receive a copy of the value as it is at the time of running the setup code, and it will always fail. 如果要传递通过副本传递的structenum ,则谓词块将在运行安装代码时按原样接收值的副本,并且它将始终失败。

If instead you prefer to use UI tests as suggested by @Randall Wang in his answer, then this post might be useful for you: " How to test UI changes in Xcode 7 ". 相反,如果您更喜欢使用@Randall Wang在其答案中建议的UI测试,则此文章可能对您有用:“ 如何在Xcode 7中测试UI更改 ”。 Full disclosure, I wrote that post. 完全公开,我写了那个帖子。

First of all, You don't need test private method 首先,您不需要测试私有方法

If you want to test if the button is hidden or not,try UI testing 如果要测试按钮是否隐藏,请尝试进行UI测试

here is the WWDC of UI testing. 这是UI测试的WWDC。

https://developer.apple.com/videos/play/wwdc2015/406/ https://developer.apple.com/videos/play/wwdc2015/406/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM