web-dev-qa-db-ja.com

XCTestのUIStoryboardからカスタムUIViewControllerをキャストできません

Swift、Xcode6-Beta5では、あまりクリエイティブな名前ではなく、「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を直接インスタンス化することもできましたが、テストの目的の1つであるIBOutlet処理が行われず、名前を変更したり、ストーリーボードエディターで接続を削除したりして、リンクが壊れないようにしています。または私が物事を壊すために見つけた他の多くの方法...

編集----私は新しいプロジェクトから始め、iOSアプリケーション->シングルビューアプリケーションテンプレートを選択しました。

TestExampleを以下に示すコードに置き換え、ViewControllerをテストターゲットに追加しました。同様の結果。このテンプレートには、ViewControllerタイプの単一のView Controllerがあり、ストーリーボードには他に何もありません。

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

    }

AnyVCの値が設定された直後のブレークポイントでの次のlldb出力:

(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

私はすべてを公開するよりも良い解決策を思いついた。実際には、nilを使用する代わりに、テストバンドルからのストーリーボードを使用する必要があります(メインターゲットのバンドルからのストーリーボードを強制します)。

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

ストーリーボードIDを設定している場合は、このようにキャストできます。

また、ビューコントローラクラスの名前をたとえばここに変更する必要があります。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
0
wpj

ソリューションの鍵を提供してくれた@DerrikHathawayに感謝します。アプリケーションとテストターゲットは異なるモジュールです。

私のアプリケーションの名前はTestingViewControllersです。この問題を修正するために、次の変更を加え、アプリケーションコードでViewControllerを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")

}

テストケースに合格

0
ahalls