Ios 如何测试视图控制器是否被解除或弹出

Ios 如何测试视图控制器是否被解除或弹出,ios,swift,uikit,Ios,Swift,Uikit,我想为我的函数编写一个单元测试,下面是代码: func smartBack(animated: Bool = true) { if isModal() { self.dismiss(animated: animated, completion: nil) } else { self.navigationController?.popViewController(animated: animated) } } 此方法会自动选择“驳回”或“弹

我想为我的函数编写一个单元测试,下面是代码:

  func smartBack(animated: Bool = true) {
    if isModal() {
      self.dismiss(animated: animated, completion: nil)
    } else {
      self.navigationController?.popViewController(animated: animated)
    }
  }

此方法会自动选择“驳回”或“弹出”。那么,我如何检查viewcontroller在完成此功能后是否弹出或关闭?感谢您的帮助

您可以使用属性self.isBeingPresented,如果按下按钮,将返回true,否则返回false。

您可以检查viewControllers堆栈,查看是否包含viewControllers,方法是:

self.navigationController.viewControllers


这将返回navigationController堆栈中包含的
[UIViewController]

您可以在其
视图将显示
视图显示
函数中检查视图控制器的
IsBeingDisembled
属性


请参阅。

我个人会使用mock跟踪某些方法的调用时间

您可以这样做:

class MockNavigationController: UINavigationController {

    var _popCalled: Bool = false
    override func popViewController(animated: Bool) -> UIViewController? {
        _popCalled = true
        return self.viewControllers.first
    }
}
然后,每当代码调用popViewController时,
\u popCalled
值都会更新,但实际上不会弹出任何内容。因此,您可以断言
\u popCalled
值,以确保发生了预期的调用

这使得测试预期的事情是否发生变得容易,并且还阻止您在测试中运行实际的代码。这种方法可以很容易地成为服务调用、数据库更新、设置标志等,因此更安全

但一开始它们可能很难理解。我建议在大量使用之前仔细阅读它们

操场上的完整示例:

import UIKit
import PlaygroundSupport
import MapKit

class ClassUnderTest: UIViewController {
    var isModal: Bool = false

    func smartBack(animated: Bool = true) {
        if isModal {
            self.dismiss(animated: animated, completion: nil)
        } else {
            self.navigationController?.popViewController(animated: animated)
        }
    }
}

class MockNavigationController: UINavigationController {

    var _popCalled: Bool = false
    override func popViewController(animated: Bool) -> UIViewController? {
        _popCalled = true
        return self.viewControllers.first
    }
}

class MockClassUnderTest: ClassUnderTest {

    var _mockNavigationController = MockNavigationController()
    override var navigationController: UINavigationController? {
        return _mockNavigationController 
    }

    var _dismissCalled: Bool = false
    override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
        _dismissCalled = true
    }

}

var subject = MockClassUnderTest()
subject.isModal = true
subject.smartBack();
var navigation = subject.navigationController as! MockNavigationController

print(subject._dismissCalled)
print(navigation._popCalled)
输出

输出


在本例中,您将重写在任何一种情况下都将调用的dislose和pop方法。在单元测试中,您只需断言存根值(_popCalled)是否符合您的期望。

我用这种方式解决了这个问题。我需要测试一个包含以下内容的简单方法:
dismission(动画:true,完成:nil)
,我制作了一个临时模拟,模拟一个viewController,它推送到我的主控制器,我在那里应用dismissView

 func testValidatesTheDismissOfViewController() {
            // Given
            let mockViewController: UIViewController = UIViewController()
            let navigationController = UINavigationController(rootViewController: mockViewController)
// Create instance of my controller that is going to dismiss.
            let sut: HomeWireFrame = HomeWireFrame().instanceController()
            navigationController.presentFullScreen(sut, animated: true)
            // When
            sut.loadViewIfNeeded()
            sut.closeView()
            // Create an expectation...
            let expectation = XCTestExpectation(description: "Dismiss modal view: HomeViewController")
            // ...then fulfill it asynchronously
            DispatchQueue.main.async { expectation.fulfill() }
            wait(for: [expectation], timeout: 1)
            // Then - if its different of my home controller
            XCTAssertTrue(!(navigationController.topViewController is HomeViewController))
          }
我希望能有所帮助,我在这里解答任何疑问。

func smartBack(动画:Bool=true)将:

func smartBack(animated: Bool = true) {  
  if self.navigationController?.viewControllers.first == self {
                self.dismiss(animated: animated, completion: nil)
            } else {
                self.navigationController?.popViewController(animated: true)
            }
}

一种解决方案是使用eventsu可以检查self.navigationController?.topViewController值是否要在viewController内部检查?如果它被解雇或被爆了?或者您想检查viewController何时刚刚移除,而不是关注调用的方法,测试效果-视图控制器是否不再显示?显示的视图控制器不包含在导航堆栈中这有助于确定视图是否被推送/显示。没有被驳回或驳回。我认为问题只是问如何编写一个检查条件的
if
语句,而不是如何执行单元测试。不太确定,问题是“那么,我如何检查viewcontroller在这个函数之后是否被驳回或驳回?”但是,是的,谢谢,这是一个很好的模拟示例。它显示所需的方法已被调用。但是,我认为更好的方法是检查视图控制器是否不再显示。测试效果。是的,总是有选择的,我想这取决于个人偏好。我以前在测试这种效果时遇到过一些问题,当显示的视图控制器包装在导航控制器中时,技术上不会返回您在isBeingPresented和类似功能上所期望的结果,所以我自己更喜欢这个选项。如果您想了解其他人如何在回答中测试效果,您会很感兴趣。虽然此代码可能会解决OP的问题,但最好包含一个解释,说明您的代码如何解决OP的问题。通过这种方式,未来的访问者可以从您的帖子中学习,并将其应用到自己的代码中。因此,它不是一种编码服务,而是一种知识资源。此外,高质量、完整的答案更有可能被提升。这些特性,以及所有帖子都是独立的要求,是SO作为一个平台的一些优势,使其区别于论坛。您可以编辑以添加其他信息和/或用源文档补充说明。我进行编辑。现在,我的代码是第一个问题“那么,如何检查视图控制器是否弹出或被解除”的答案:答案是我的func smartBack(动画:Bool=true):
 func testValidatesTheDismissOfViewController() {
            // Given
            let mockViewController: UIViewController = UIViewController()
            let navigationController = UINavigationController(rootViewController: mockViewController)
// Create instance of my controller that is going to dismiss.
            let sut: HomeWireFrame = HomeWireFrame().instanceController()
            navigationController.presentFullScreen(sut, animated: true)
            // When
            sut.loadViewIfNeeded()
            sut.closeView()
            // Create an expectation...
            let expectation = XCTestExpectation(description: "Dismiss modal view: HomeViewController")
            // ...then fulfill it asynchronously
            DispatchQueue.main.async { expectation.fulfill() }
            wait(for: [expectation], timeout: 1)
            // Then - if its different of my home controller
            XCTAssertTrue(!(navigationController.topViewController is HomeViewController))
          }
func smartBack(animated: Bool = true) {  
  if self.navigationController?.viewControllers.first == self {
                self.dismiss(animated: animated, completion: nil)
            } else {
                self.navigationController?.popViewController(animated: true)
            }
}