2

I'm looking for a "best practice" / "low test friction" way to do state based testing on view controllers inside my base AppDelegate class. Currently the below provides an easy way to stub in my own UIViewController (using ocmock) when something happens to it inside a method on the class.

-(FirstViewController *)getFirstViewController
{
    if (self.viewController1)
    { return self.viewController1; }

    self.viewController1 = [[FirstViewController alloc] initWithNibName:@"FirstViewController" bundle:nil];

    return self.viewController1;
}

The first question I have -Is this a valid way to stub out / inject my own mock view controller for testing? (seems to work great but I'm not sure if this is how the pros are doing state based testing today)

The next question I have -Is it valid to keep 1 copy of the view controller in memory like this (only creating it from scratch once for the life of the app) ?

**note- I would dependency inject this but my init is already large enough just injecting the nav controller and tab bar controller so that's not an option for this large class sadly

Toran Billups
  • 27,111
  • 40
  • 155
  • 268

3 Answers3

1

Dependency injection doesn't require you to inject all dependencies through the init method. There are reasons why that's preferred but that's another discussion.

You could simply add a -setFirstViewController: method to your class. You would use that method in your test to inject your mock. If you don't like that method being around in your app you can add the method using a category in your test code.

Erik Doernenburg
  • 2,933
  • 18
  • 21
  • fair point -but if I remove this method and do basic setter injection how does that play w/ the 2nd part of my question about when to new up these dependencies in the AppDelegate? – Toran Billups Mar 18 '12 at 22:12
  • You can still keep the lazy-init in the getter you've included above. Just make sure that in your test you call the setter I propose before the getter is invoked for the first time. – Erik Doernenburg Mar 19 '12 at 15:52
1

If it's the root view controller, you should just make it a property of your app delegate:

@interface MyAppDelegate : NSObject <UIApplicationDelegate>
@property(retain)FirstViewController *firstViewController;
@end

@implementation MyAppDelegate
@synthesize firstViewController;
...
@end

Unless the method you're testing is the method where you initialize firstViewController, you don't need any kind of lazy loading approach. You just get the app delegate in your test, create an instance of FirstViewController and assign it to the property on your delegate, and define the test:

-(void)testSomething {
    MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    FirstViewController *firstViewController = [[FirstViewController alloc] init];
    appDelegate.firstViewController = firstViewController;

    // test some app delegate method
    ...
}

If you want to mock out the controller for whatever you're testing, you can do that as well:

-(void)testSomething {
    MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    id mockController = [OCMockObject mockForClass:[FirstViewController class]];
    appDelegate.firstViewController = mockController;

    [[mockController expect] someControllerMethod];

    // test some app delegate method
    ...

    [mockController verify];
}
Christopher Pickslay
  • 17,523
  • 6
  • 79
  • 92
0

For this kind of test I will make it like you, well, slighly differently.

1st- lazy loading of First View controller is encapsulated insite a property.

In .h file

@interface AppDelegate {
    FirstViewController *viewController1_;
}

Then

@property (nonatomic, readonly) FirstViewController viewController1;

In .m file

- (FirstViewController *)viewController1 {
    if (!viewController1_) {
        viewController1_ = [[FirstViewController alloc] initWithNibName:@"FirstViewController" bundle:nil];
    }

    return viewController1_;
}

2nd- If I want to inject a mock object, I use KVC in my test code

[appDelegateUnderTest setValue:mockViewController forKey:@"viewController1_"];

Regards,

Quentin
  • 1,741
  • 2
  • 18
  • 29