Невозможно запустить пользовательский UIViewController из UIStoryboard в XCTest

В Swift, Xcode6-Beta5, я пытаюсь unit test мой "ViewController", а не очень творческое имя.

От взгляда на другие ответы, я думаю, что у меня не настроено целевое задание "Тесты".

Ошибка кода проверки:

  func testItShouldLoadFromStoryboard() {

    var storyBoard: UIStoryboard?
    var anyVC: AnyObject?
    var viewController: ViewController?
    var uiViewController: UIViewController?

    storyBoard = UIStoryboard(name:"Main", bundle: nil)
    XCTAssert(storyBoard != nil, "Test Not Configured Properly")

    anyVC = storyBoard?.instantiateInitialViewController()
    viewController = anyVC  as? ViewController
    // Failing Assertion 
    XCTAssert(viewController != nil, "Test Not Configured Properly")

    uiViewController = anyVC as? UIViewController
    XCTAssert(uiViewController != nil, "Test Not Configured Properly")

  }

Я могу заставить трансляцию со следующими строками:

anyVC = storyBoard?.instantiateInitialViewController()
viewController = (anyVC != nil) ? (anyVC  as ViewController) : nil

Но это вызвало следующий сбой:

libswiftCore.dylib`swift_dynamicCastClassUnconditional:
0x10724f5a0:  pushq  %rbp
0x10724f5a1:  movq   %rsp, %rbp
0x10724f5a4:  pushq  %r14
0x10724f5a6:  pushq  %rbx
0x10724f5a7:  movq   %rsi, %rbx
0x10724f5aa:  movq   %rdi, %r14
0x10724f5ad:  testq  %r14, %r14
0x10724f5b0:  je     0x10724f5de               ; swift_dynamicCastClassUnconditional + 62
0x10724f5b2:  movabsq $-0x7fffffffffffffff, %rax
0x10724f5bc:  andq   %r14, %rax
0x10724f5bf:  jne    0x10724f5de               ; swift_dynamicCastClassUnconditional + 62
0x10724f5c1:  movq   %r14, %rdi
0x10724f5c4:  callq  0x107279a6e               ; symbol stub for: object_getClass
0x10724f5c9:  nopl   (%rax)
0x10724f5d0:  cmpq   %rbx, %rax
0x10724f5d3:  je     0x10724f5ed               ; swift_dynamicCastClassUnconditional + 77
0x10724f5d5:  movq   0x8(%rax), %rax
0x10724f5d9:  testq  %rax, %rax
0x10724f5dc:  jne    0x10724f5d0               ; swift_dynamicCastClassUnconditional + 48
0x10724f5de:  leaq   0x3364d(%rip), %rax       ; "Swift dynamic cast failed"
0x10724f5e5:  movq   %rax, 0xa456c(%rip)       ; gCRAnnotations + 8
0x10724f5ec:  int3   
0x10724f5ed:  movq   %r14, %rax
0x10724f5f0:  popq   %rbx
0x10724f5f1:  popq   %r14
0x10724f5f3:  popq   %rbp
0x10724f5f4:  retq   
0x10724f5f5:  nopw   %cs:(%rax,%rax)

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

РЕДАКТИРОВАТЬ ---- Я начал с нового проекта, выбрал приложение IOS Application → Single View Application.

Заменил testExample кодом, как показано ниже, добавлен ViewController в тестовый объект. Аналогичные результаты. Этот шаблон имеет единый контроллер вида типа ViewController, больше ничего в раскадровке.

  func testExample() {


      var storyBoard: UIStoryboard?
      var anyVC: AnyObject?
      var viewController: ViewController?

      storyBoard = UIStoryboard(name:"Main", bundle: nil)
      XCTAssert(storyBoard != nil, "Test Not Configured Properly")

      anyVC = storyBoard?.instantiateInitialViewController()
      viewController = anyVC as? ViewController
      XCTAssert(viewController != nil, "Test Not Configured Properly")

      // This is an example of a functional test case.
      XCTAssert(true, "Pass")

    }

Следующий вывод lldb в точке останова сразу после значения anyVC:

(lldb) po anyVC
(instance_type = Builtin.RawPointer = 0x00007fe22c92a290 -> 0x000000010e20bd80 (void *)0x000000010e20bea0: OBJC_METACLASS_$__TtC22TestingViewControllers14ViewController)
 {
  instance_type = 0x00007fe22c92a290 -> 0x000000010e20bd80 (void *)0x000000010e20bea0: OBJC_METACLASS_$__TtC22TestingViewControllers14ViewController
} 

Ответы

Ответ 1

Я придумал лучшее решение, чем сделать все публичным. На самом деле вам просто нужно использовать раскадровку, которая исходит из тестового пакета, вместо того, чтобы использовать nil (заставляя его исходить из основного целевого пакета). Я сделал более подробное сообщение здесь, если кто-то заинтересован в чтении немного дальше.

var storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle(forClass: self.dynamicType))
vc = storyboard.instantiateViewControllerWithIdentifier("LoginVC") as LoginViewController
vc.loadView()

Ответ 2

Спасибо @Derrik Hathaway за ключ к решению. Приложение и тестовая цель - разные модули.

Мое приложение имеет неудачное имя TestingViewControllers. Чтобы исправить ошибку, я внесла следующие изменения и сделал класс ViewController a public еще в коде приложения.

Добавлена ​​эта строка для проверки:

import TestingViewControllers

Измените тестовый пример:

func testExample() {


  var storyBoard: UIStoryboard?
  var anyVC: AnyObject?
  var viewController: TestingViewControllers.ViewController?

  storyBoard = UIStoryboard(name:"Main", bundle: nil)
  XCTAssert(storyBoard != nil, "Test Not Configured Properly")

  anyVC = storyBoard?.instantiateInitialViewController()
  viewController = anyVC as? TestingViewControllers.ViewController
  XCTAssert(viewController != nil, "Test Not Configured Properly")

  // This is an example of a functional test case.
  XCTAssert(true, "Pass")

}

Прохождение тестового теста

Ответ 3

Установили ли вы раскадровку, если у вас есть, тогда вы можете сделать это следующим образом.

Вам также нужно переименовать класс контроллера вида, например, здесь MyAppViewController использует основную раскадровку вместо стандартного класса viewcontroller, а затем устанавливает идентификатор storyboardID, например, MyappVC

  var sut : MyAppViewController!

override func setUp() {
    super.setUp()

    // get a reference to storyboard and the correct
    // viewcontroller inside it. Remember to add
    // storyboardID in this case "MyappVC"


    let StoryBoard = UIStoryboard.init(name: "Main", bundle: nil)
    sut = StoryBoard.instantiateViewControllerWithIdentifier("Myapp VC")as!
    CalculatorViewController

    //trigger viewDidLoad()
    _ = sut.view