Как ввести подкласс UIViewController в Swift?

#ios #testng #swift

#iOS #testng #swift

Вопрос:

Я пытаюсь протестировать подкласс UITableViewController в swift с помощью раскадровки. Я могу получить ссылку на контроллер представления и зарегистрировать его, но я не могу ввести его в класс, который я пытаюсь протестировать, Поэтому я не могу получить доступ к методам, свойствам и т. Д.

Как правильно получить доступ к экземпляру моего класса?

Тестовый сценарий:

 import XCTest
import UIKit    
class GameListControllerTest: XCTestCase {
    var storyboard = UIStoryboard(name: "Main", bundle: nil)
    var _sut: AnyObject?
    var sut: AnyObject {
        if(_sut?) {
            return _sut!
        }
        _sut = storyboard.instantiateViewControllerWithIdentifier("GameListController")
        return _sut!
    }
    override func setUp() {
        super.setUp()
        println()
        println("=================================")
        println("sut  (sut)")
        println("view  (sut.view)")
        println("=================================")

    }
    func testInstance() {
        var vc1 = sut as UITableViewController
        var vc2 = sut as? GameListController
        println()
        println("=================================")
        println("VC1  (vc1)")
        println("VC2  (vc2)")
        println("=================================")
    }
}
  

Вывод:

 XCTestOutputBarrierTest Suite '_TtC15mytabletopTests22GameListControllerTest' started at 2014-06-16 06:13:30  0000
XCTestOutputBarrierTest Case '-[_TtC15mytabletopTests22GameListControllerTest testInstance]' started.
XCTestOutputBarrier
=================================
sut  <_TtC10mytabletop18GameListController: 0xb338d50>
view  <UITableView: 0xc033800; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = W H; gestureRecognizers = <NSArray: 0xb344960>; layer = <CALayer: 0xb33c7d0>; contentOffset: {0, 0}; contentSize: {480, 0}>
=================================

=================================
VC1  <_TtC10mytabletop18GameListController: 0xb338d50>
VC2  nil
=================================
Test Case '-[_TtC15mytabletopTests22GameListControllerTest testInstance]' passed (0.007 seconds).
XCTestOutputBarrierTest Suite '_TtC15mytabletopTests22GameListControllerTest' passed at 2014-06-16 06:13:30  0000.
     Executed 1 test, with 0 failures (0 unexpected) in 0.007 (0.010) seconds
  

Если я попытаюсь принудительно ввести typecast ( sut as GameListController ), я получу исключение во время выполнения.

Комментарии:

1. В нем говорится, что переменная имеет тип TtC10mytabletop18GameListController , а не GameListController. Вся эта ерунда на фронте что-нибудь значит для вас? Есть ли у вас это где-нибудь в вашем проекте?

2. Для меня это больше похоже на имя экземпляра, чем на класс. Для меня выглядит так: TtC10 name_of_my_app 18 identifier_on_storyboard .

3. Это искаженное имя вашего класса. Имена классов искажаются в модуле путем добавления имени модуля и некоторой информации о длине (10 и 18)

Ответ №1:

Вот что происходит. Мне потребовалось несколько дней, чтобы разобраться, но моя проблема заключалась в том, как мои классы экспортировались в целевые объекты:

Неверное целевое членство инспектора файлов против Правильно

Это вызывало две двоичные копии моего класса, одну в целевом приложении, а другую в тестовой цели. Если мы уделим больше внимания журналам, мы можем заметить:

 Test Case '-[_TtC15mytabletopTests22GameListControllerTest testInstance]'
  

Выше приведен метод тестирования testInstance , и он является частью контекста mytabletopTests выполнения. Теперь давайте посмотрим на экземпляр, извлеченный из раскадровки:

 sut  <_TtC10mytabletop18GameListController: 0xb338d50>
  

Это, в свою очередь, выполняется в mytabletop контексте. Это объясняет, почему тесты не могут типизировать GameListController . То GameListController , о чем он знает, — это тот, который скомпилирован внутри тестовой цели.

Поскольку удаление класса из целевого объекта тестирования делает класс неизвестным для моего тестового примера, теперь мне нужно импортировать цель моего приложения в тестовый пример:

 import XCTest
import UIKit
import mytabletop // LINE ADDED

class GameListControllerTest: XCTestCase {
  

Теперь единственное GameListController , к чему имеет доступ тест, — это тот же, к которому создается экземпляр раскадровки, и я, наконец, могу ввести его. Вот новый тестовый пример:

 import XCTest
import UIKit
import mytabletop

class GameListControllerTest: XCTestCase {
    let sut: GameListController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("GameListController") as GameListController

    override func setUp() {
        super.setUp()
        UIApplication.sharedApplication().keyWindow.rootViewController = sut
        XCTAssertNotNil(sut.view)
    }

    func testInstance() {
        XCTAssertNotNil(sut)
        XCTAssertNotNil(sut.tableView) // UITableViewController property
        XCTAssertNotNil(sut.store) // instance property
        XCTAssertNotNil(sut.someButton) // outlet
    }
}
  

Теперь я могу правильно ввести тип (прокрутите, чтобы увидеть as GameListController ) во время инициализации экземпляра. Чтобы заставить все выходы быть привязанными правильно, а вложенные представления отображаться в соответствии с устройством, на котором выполняется тест, мы можем сделать контроллер представления rootViewController для приложения и извлечь из него представление, как показано в setUp функции выше. Even myCustomOutlet теперь работает правильно.