View Controller TDD

Ich versuche, meinem Projekt einige Komponententests hinzuzufügen, um View-Controller zu testen. Allerdings habe ich anscheinend Probleme mit scheinbar einfachen Dingen. Ich habe ein Beispielprojekt erstellt, auf das ich verweisen werde.https: //github.com/pangers/ViewControllerTestin

Das Beispiel enthält einen UINavigationController als anfänglichen Ansichtscontroller. Der Root-View-Controller des UINavigationControllers ist FirstViewController. Auf FirstViewController befindet sich eine Schaltfläche, die zu SecondViewController wechselt. In SecondViewController gibt es ein leeres Textfeld.

Die zwei Tests, die ich hinzufügen möchte, sind:
1) Überprüfen Sie, ob der Schaltflächentitel in FirstViewController "Nächster Bildschirm" ist.
2) Überprüfen Sie, ob das Textfeld in SecondViewController leer ist, "".

Ich habe gehört, dass das Hinzufügen Ihrer schnellen Dateien sowohl zum Hauptziel als auch zum Testziel keine gute Praxis ist. Es ist jedoch besser, das, worauf Sie in Ihren Tests zugreifen möchten, öffentlich zu machen und das Hauptziel in die Tests zu importieren. Das habe ich also getan. (Ich habe auch das "Defines Module" für das Hauptziel auf YES gesetzt, da ich dies auch in einigen Artikeln gelesen habe.)

In FirstViewControllerTests habe ich den ersten View-Controller wie folgt instanziiert:

var viewController: FirstViewController!

override func setUp() {
    let storyboard = UIStoryboard(name: "Main", bundle: NSBundle(forClass: self.dynamicType))
    let navigationController = storyboard.instantiateInitialViewController() as UINavigationController
    viewController = navigationController.topViewController as FirstViewController
    viewController.viewDidLoad()
}

Und ich habe den Test hinzugefügt:

func testCheckButtonHasTextNextScreen() {
    XCTAssertEqual(viewController.button.currentTitle!, "Next Screen", "Button should say Next Screen")
}

Ähnlich habe ich es für SecondViewControllerTest eingerichtet mit:

var secondViewController:SecondViewController!

override func setUp() {
    let storyboard = UIStoryboard(name: "Main", bundle: NSBundle(forClass: self.dynamicType))
    let navigationController = storyboard.instantiateInitialViewController() as UINavigationController
    let firstviewController = navigationController.topViewController as FirstViewController
    firstviewController.performSegueWithIdentifier("FirstToSecond", sender: nil)
    secondViewController = navigationController.topViewController as SecondViewController
    secondViewController.viewDidLoad()
}

Und der Test:

func testTextFieldIsBlank() {
    XCTAssertEqual(secondViewController.textField.text, "", "Nothing in textfield")
}

Sie beide scheitern und ich bin mir nicht sicher warum. Mein Verdacht ist, dass die Art und Weise, wie ich die View Controller instanziiere, nicht korrekt ist. Ist die beste Möglichkeit, die View Controller zu instanziieren, die Verwendung des Storyboards (genau wie bei einer Ausführung im realen Leben)? Oder ist es akzeptabel, instanziiert zu werden über:

var viewController = FirstViewController()

Welche Erfahrungen habt ihr mit TDD und View Controllern in kürzester Zeit?

Ich benutze Swift mit XCode 6.1.1.

Danke im Voraus

Gelöst

Ok, nachdem ich die Antworten von Modocache und Mike Taverne geprüft habe, habe ich meine Lösung gefunden und ein paar Dinge gelernt, die ich unten aufschreiben werde.

1) Ich habe alle Klassen / Methoden / Variablen erstellt, die ich öffentlich testen möchte. Ich muss die schnellen Dateien nicht zum Testziel hinzufügen.

2) Ich musste nur "Defines Module" für das "Main" -Ziel festlegen (im Gegensatz zum "Test" -Ziel oder dem gesamten Projekt)

3) Wenn Sie das Storyboard instanziieren, sollte das Bundle auf Null und nicht auf NSBundle (forClass: self.dynamicType) gesetzt werden, da sonst die Tests fehlschlagen.

4) Wie im Modocache angegeben, ist es gut, dem View Controller eine StoryboardID zu geben und sie wie folgt zu instanziieren:

viewController = storyboard.instantiateViewControllerWithIdentifier("FirstViewController") as FirstViewController

Wenn Sie jedoch den View Controller auf diese Weise instanziieren, instanziiert er NUR den View Controller und nicht die Navigationscontroller, in die er möglicherweise eingebettet ist. Das bedeutet, dass Sie versuchen,

XCTAssertFalse(viewController.navigationController!.navigationBarHidden, "Bar should show by default")

wird zu keiner Ausnahme führen. Ich bestätigte dies mit

XCTAssertNil(viewController.navigationController?, "navigation controller doesn't exist")

was zu einem erfolgreichen Test führte.

Da ich den Status der Navigationsleiste in FirstViewController überprüfen wollte, müssen Sie den View Controller folgendermaßen instanziieren:

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let navigationController = storyboard.instantiateInitialViewController() as UINavigationController
viewController = navigationController.topViewController as FirstViewController

Jetzt den Test durchführen

XCTAssertFalse(viewController.navigationController!.navigationBarHidden, "nav bar should be showing by default")

Ergebnisse in einem erfolgreichen Test.

5) let _ = viewController.view löst tatsächlich viewDidLoad () aus, was durch einen Test bestätigt wurde

6) let _ = viewController.view löst nicht viewWillAppear () aus und ich gehe auch danach von irgendetwas aus. viewController.viewWillAppear (false / true) muss manuell aufgerufen werden, um es auszulösen (durch einen Test bestätigt).

Hoffentlich wird dies den Menschen helfen. Ich werde das aktualisierte Projekt an GitHub senden (Link oben), wenn jemand damit herumspielen möchte.

Update # 2

Nach alledem konnte ich immer noch nicht herausfinden, wie ich vom ersten Ansichtscontroller zum zweiten Ansichtscontroller wechseln soll (damit ich die Eigenschaften der Navigationsleiste in SecondViewControllerTests.swift testen kann). Ich habe es versuch

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let nc = storyboard.instantiateInitialViewController() as UINavigationController
let firstVC = nc.topViewController as FirstViewController
firstVC.performSegueWithIdentifier("FirstToSecond", sender: nil)
secondVC = nc.topViewController as SecondViewController

was einen Fehler verursachte.

Ich habe es auch versucht

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let nc = storyboard.instantiateInitialViewController() as UINavigationController
let firstVC = nc.topViewController as FirstViewController
firstVC.toSecondVCButton.sendActionsForControlEvents(UIControlEvents.TouchUpInside)
secondVC = nc.topViewController as SecondViewController

was hat nicht funktioniert.

Ich habe es schließlich versucht

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let nc = storyboard.instantiateInitialViewController() as UINavigationController
vc = storyboard.instantiateViewControllerWithIdentifier("Second") as SecondViewController
nc.pushViewController(vc, animated: false)
let _ = vc.view
vc.viewWillAppear(false)

Das hat perfekt mit meinen Tests funktioniert (ich konnte auf die Eigenschaften der Navigationsleiste zugreifen)!