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
}
私はすべてを公開するよりも良い解決策を思いついた。実際には、nilを使用する代わりに、テストバンドルからのストーリーボードを使用する必要があります(メインターゲットのバンドルからのストーリーボードを強制します)。
var storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle(forClass: self.dynamicType))
vc = storyboard.instantiateViewControllerWithIdentifier("LoginVC") as LoginViewController
vc.loadView()
ストーリーボード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
ソリューションの鍵を提供してくれた@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")
}
テストケースに合格