deinit вызывается в приложении, но не в модульном тесте при отклонении контроллера вида - PullRequest
0 голосов
/ 25 апреля 2019

Я пытаюсь выполнить модульное тестирование памяти моих контроллеров представлений, чтобы определить, правильно ли они деинициализируются при прекращении работы.

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 Ответ

1 голос
/ 25 апреля 2019

controller - это строгое свойство, поэтому ваш тест сохраняет его.Прямо сейчас он получает deinit'd только при последующих вызовах на 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 переменные и свойства должны быть Optional, но наличие необязательного значения не означает weak.См. Слабые ссылки .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...