关闭视图控制器时,在应用中调用了deinit,但未在单元测试中调用

时间:2019-04-24 22:32:12

标签: ios swift unit-testing uikit

我正在尝试对视图控制器的内存进行单元测试,以查看它们在被关闭时是否正确地初始化。

class SettingsViewControllerTests: XCTestCase {

    var controller: SettingsViewController!

    override func setUp() {
        super.setUp()

        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        controller = storyboard.instantiateViewController(withIdentifier: "SettingsViewController")
            as? SettingsViewController

        //load view hierarchy
        _ = controller.view
    }

    func testLogout() {
        let sideMenu = MockSideMenuViewController()
        var navController: UINavigationController? = UINavigationController(rootViewController: sideMenu)
        sideMenu.show(navController!, sender: nil)
        navController?.pushViewController(controller, animated: true)
        expect(navController).toNot(beNil())
        controller.dismiss(animated: false, completion: nil)

        expect(navController).toEventually(beNil(), timeout: 3) // fails
        expect(self.controller).toEventually(beNil(), timeout: 3) // fails
    }

在应用程序中,我使用segue展示了我的导航控制器+ SettingsViewController。它有一个自身调用dismiss的方法,但是当我检查控制器实例时,它们仍然存在。在我的视图控制器中,我设置了一条打印语句来检查是否调用了deinit,并在应用程序上执行这些步骤时,它确实被调用了(两个控制器最终都转到nil)。但是,单元测试不能做到这一点。我想念什么?

1 个答案:

答案 0 :(得分:1)

controller是一个严格控制的属性,因此您的测试将保留它。现在,它仅在随后对setUp()的调用中被取消初始化。如果您想专门测试controller.deinit,请在测试中进行controller = nil

您可能可以执行以下操作:

var controller: SettingsViewController!  // <--- this is a strong ref

func testLogout() {
        let sideMenu = MockSideMenuViewController()

        // navController is a strong ref, held until the end of the scope; don't expect it to be nil
        var navController: UINavigationController? = UINavigationController(rootViewController: sideMenu)
        sideMenu.show(navController!, sender: nil)
        navController?.pushViewController(controller, animated: true)

        // hold a weak ref to your controller and then nil out its reference
        weak var weakController = controller

        // remove the strong reference
        controller = nil

        // popping will release the last reference 
        navController?.popViewController(animated: false)

        expect(weakController).to(beNil(),) // succeeds
    }

一些注意事项:

  • navController是作用域级别的变量。直到函数结束才为零,因此没有理由进行测试或期望。创建它之后,它肯定也不会为零。

  • UIViewController.dismiss(...)用于消除模态。您的控制器是导航堆栈的一部分。开除不会达到您的期望。

感觉像您期望var navController: UINavigationController?一样薄弱,但事实并非如此。 weak的vars和properties应该为Optional,但为Optional并不意味着weak。参见Weak References