web-dev-qa-db-fra.com

Impossible de transtyper UIViewController personnalisé à partir d'UIStoryboard dans XCTest

Dans Swift, Xcode6-Beta5, j'essaie de tester mon "ViewController", pas un nom très créatif. 

En regardant d’autres réponses, je pense que ma cible "Tests" n’est pas configurée correctement.

Code de test manquant:

  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")

  }

Je peux forcer le casting avec les lignes suivantes:

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

Mais cela a provoqué le crash suivant: 

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)

J'ai également réussi à instancier ViewController directement, mais le traitement IBOutlet, qui est l'un des objectifs de mes tests, ne permet pas de rompre la liaison en renommant des éléments, en supprimant des connexions dans l'éditeur de Storyboard, ou les nombreux autres moyens que j'ai trouvés pour casser des choses ... 

EDIT ---- J'ai commencé avec un nouveau projet, j'ai choisi le modèle Application iOS -> Application à vue unique.

Remplacé le testExample par le code comme indiqué ci-dessous, ViewController ajouté à la cible de test. Des résultats similaires. Ce modèle a un seul contrôleur de vue de type ViewController, rien d’autre dans le storyboard. 

  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")

    }

La sortie lldb suivante à un point d'arrêt juste après que la valeur de anyVC a été définie:

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

J'ai proposé une meilleure solution que de tout rendre public. En fait, vous devez simplement utiliser un scénario qui provient du groupe de tests au lieu de zéro (le forcer à provenir du groupe de la cible principale).

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

Avez-vous défini le storyboardID si vous avez alors vous pouvez le lancer comme ça.

Vous devez également renommer votre classe de contrôleur de vue, par exemple ici. MyAppViewController fait en sorte que le storyboard principal l’utilise au lieu de la classe standard viewcontroller, puis définisse le storyboardID sur MyappVC, par exemple.

  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
0
wpj

Merci @Derrik Hathaway pour la clé de la solution. L'application et la cible de test sont des modules différents. 

Mon application porte le nom malheureux TestingViewControllers. Pour corriger le problème, j'ai apporté les modifications suivantes et fait de ViewController une classe public dans le code de l'application.

Ajout de cette ligne pour tester le cas:

import TestingViewControllers

Remplacez le scénario de test par ceci:

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")

}

Cas de test passe

0
ahalls