jwfriese / Fleet

Storyboard injection and helpful extensions to enable fast, consistent UIKit testing in iOS
Apache License 2.0
24 stars 6 forks source link

Unable to bind fake view controllers in test #2

Closed gdean123 closed 8 years ago

gdean123 commented 8 years ago

We are attempting to use Fleet's bindViewController to mock out view controllers that we segue to in our tests. Unfortunately, the assertions that we make to prove that we have performed the segue do not seem to work:

class PaymentsViewControllerSpec: SwinjectSpec {
    override func spec() {
        describe("PaymentsViewController") {
            var subject: PaymentsViewController!
            var creditCardViewController: CreditCardViewController!

            beforeEach {
                let paymentStoryboard = UIStoryboard(name: "Payment", bundle: NSBundle.mainBundle())
                creditCardViewController = CreditCardViewController()
                try! paymentStoryboard.bindViewController(creditCardViewController, toIdentifier: "CreditCardViewController")

                subject = self.instantiateController("PaymentsViewController", storyboardName: "Payment") as! PaymentsViewController

                let window = UIWindow()
                window.rootViewController = subject
                window.makeKeyAndVisible()
                _ = subject.view
            }

            it("segues to the add credit card page") {
                didSelectRowAt(creditCards.count + 1)
                let topmostViewController = Fleet.getCurrentScreen()?.topmostPresentedViewController

                // FAILS: view controllers have different memory addresses
                expect(topmostViewController).to(beIdenticalTo(creditCardViewController)) 

                // FAILS: presented view controller is nil
                expect(subject.presentedViewController).to(beIdenticalTo(creditCardViewController))
            }
        }
    }
}

Here is approximately what our implementation looks like:

class PaymentsViewController: UIViewController, UITableViewDelegate {
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        performSegueWithIdentifier("showCreditCard", sender: nil)
    }
}

We are doing this on a view controller that has a direct segue from PaymentsViewController to the CreditCardViewController.

Maybe a sample project would be helpful to show how this is done?

jwfriese commented 8 years ago

@gdean123 - I'm wondering now what this line is doing:

subject = self.instantiateController("PaymentsViewController", storyboardName: "Payment") as! PaymentsViewController

Seems like maybe some helper method on SwinjectSpec? What is it doing with that storyboard name? If it's spinning up an instance of a UIStoryboard under the covers, that would explain why the binding doesn't seem to be working.

I'll look at Fleet's unit testing right now to verify that it's covering this case.

jwfriese commented 8 years ago

(Sorry, hit the wrong button 😅 )

gdean123 commented 8 years ago

Sorry about that - I didn't realize that we had written that method. Here is what instantiateController does:

class SwinjectSpec: QuickSpec {
    let testContainer = Container()

    func instantiateController(controllerName: String, storyboardName: String) -> UIViewController {
        let storyboard = SwinjectStoryboard.create(name: storyboardName, bundle: nil, container: testContainer)
        let controller = storyboard.instantiateViewControllerWithIdentifier(controllerName)
        return controller
    }
}
jwfriese commented 8 years ago

@gdean123 - I'm going to close this for now, as it seems like maybe that code listed in your last comment is what was causing your issues. If that turns out to be false, please let me know and we can reopen.