Unit Tests um zu testen, ob ein ViewController gepusht wurde

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Unit Tests um zu testen, ob ein ViewController gepusht wurde

    Ich möchte testen, ob ein ViewController auf den Navigation-Stack gepusht wurde. Ich mache das zur Zeit so:

    Quellcode

    1. func testViewControllerGotPushedStoryboardVersion() {
    2. class MockNavigationController: UINavigationController {
    3. var pushedViewController: UIViewController?
    4. private override func pushViewController(viewController: UIViewController, animated: Bool) {
    5. pushedViewController = viewController
    6. super.pushViewController(viewController, animated: animated)
    7. }
    8. }
    9. let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("MasterViewController") as MasterViewController
    10. let mockNavigationController = MockNavigationController(rootViewController: viewController)
    11. let expectation = expectationWithDescription("should push")
    12. viewController.performSegueWithIdentifier("showDetail", sender: nil)
    13. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(NSEC_PER_SEC*0)), dispatch_get_main_queue()) { () -> Void in
    14. XCTAssertTrue(mockNavigationController.pushedViewController is DetailViewController, "")
    15. expectation.fulfill()
    16. }
    17. waitForExpectationsWithTimeout(1, handler: nil)
    18. }
    Alles anzeigen


    Ich finde das sieht seltsam aus. Gibt es eine bessere Lösung? Wie testet ihr das?
  • Du brauchst den NavigationController nicht zu mocken, sondern kannst direkt dessen topViewController testen:

    Quellcode

    1. - (void)testThatItNavigatesToAViewController {
    2. UIViewController *expectedViewController = ...
    3. UINavigationController *navigationController = ... // hier einfach den Navigationcontroller des storyboards nutzen
    4. [sut invokeActionWhichPushesViewControllerToNavigationController];
    5. XCTestExpectation *pushExpectation = [self expectationWithDescription:@"Push controller"];
    6. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    7. [pushExpectation fulfill];
    8. });
    9. [self waitForExpectationsWithTimeout:3 handler:^(NSError *error) {
    10. XCTAssertEqualObjects(navigationController.topViewController, expectedViewController);
    11. }];
    12. }
    Alles anzeigen

    Die assertion würde ich in den expectationTimeOutHandler packen.
    Aber wie macmoonshine schon sagte: das ist im Grunde ein Integrationstest und man verlangsamt sich den feedback loop (wenn Du auf presentedViewController testest, musst Du in dispatch_after die Animationszeit der presentation/dismissal warten). Andererseits ist die Wartung mit Calabash oder Frank auch mit Aufwand verbunden. Ist halt immer eine pragmatische Abwägungssache.
  • Markus Müller schrieb:

    Du brauchst den NavigationController nicht zu mocken, sondern kannst direkt dessen topViewController testen:


    Das funktioniert nur dann, wenn der ViewController, den ich testen möchte, der rootViewController des NavigationControllers ist. Das ist in diesem Fall tatsächlich so, muss aber nicht so sein. Und ich will im Test nicht erst an die Stelle navigieren, die ich testen will.

    Danke für den Tipp mit der assertion im expectationTimeOutHandler.

    Bezüglich Integrationstests: Was spricht eigentlich dagegen, diese auch mit XCTest zu machen? UIAutomation ist JavaScript (bäh ;)) und Frank und co. ist eine externe Abhängigkeit, die ich gerne vermeiden würde.
  • dasdom schrieb:

    Das funktioniert nur dann, wenn der ViewController, den ich testen möchte, der rootViewController des NavigationControllers ist. Das ist in diesem Fall tatsächlich so, muss aber nicht so sein. Und ich will im Test nicht erst an die Stelle navigieren, die ich testen will.

    Der topViewController ist der oberste Controller auf dem Navigationstack; diese Property liefert dir nach einem Push also immer den zuletzt gepushten Controller.
    „Meine Komplikation hatte eine Komplikation.“
  • macmoonshine schrieb:


    Der topViewController ist der oberste Controller auf dem Navigationstack; diese Property liefert dir nach einem Push also immer den zuletzt gepushten Controller.


    Ja, das stimmt. Allerdings muss ich ja erstmal zu dem ViewController navigieren, von dem aus ich puschen möchte. Oder aber ich hole mir den NavigationController aus dem Storyboard und den ViewController, von dem aus ich puschen will und setze aus den Navigation-Stack. Dann kann ich aber gleich mit einem Mock arbeiten.