2
votes

I want to write integration application test to test the full CRUD operations. I have a table view to list all objects. User can click "Add" button to push a new detail view controller to add a new object; They can click on an existing object to push a detail view controller to rename the object; They can also delete the object from the list.

I got excited to try out OCUnit to test my app. However, I have ran into some weird bugs. I am using UITabBarController + UINavigationController. I have more than 3 levels of pushViewController in my UINavigationController. If I test the code as it is with [self.navigationController pushViewController:viewController animated:YES] , it mostly works with two warnings:

nested push animation can result in corrupted navigation bar
nested pop animation can result in corrupted navigation bar

I noticed viewDidAppear isn't called hence the warnings.

Then I try to workaround by changing animated:YES to animated:NO. The warnings go away. However, I ran into another bug where viewDidLoad isn't called at all. So the view doesn't load from the NIB file and all the IB controls are nil. This wasn't the case if I set animated to YES.

This is the code I use to get a handle of the root view controller in the UINavigationController.

app = (MyAppDelegate*) [[UIApplication sharedApplication] delegate]; 
rootVC = ((MyViewController*)[[((UINavigationController*)[app.tabBarController.viewControllers objectAtIndex:0]) viewControllers] objectAtIndex:0]);

I manually called this code in my test to simulate a click on the UITableView:

[rootVC tableView:rootVC.tableView didSelectRowAtIndexPath:ip];  // push is called inside this

So I am stuck either I set animated:YES or animated:NO. Does anyone successfully use OCUnit to test apps with UINavigationController + UITableView? If so, could you please share some sample code or your experience?

1
It's hard to tell what you want to test -- whether a particular view controller is pushed or not?Jon Reid

1 Answers

7
votes

First off, for unit tests, don't get your root view controller from your app delegate. The actual root view controller is a singleton, and singletons in unit tests create inter-test dependencies. Instead, have the test set up (and tear down) its own view controller instance.

Certain kinds of tests require the view controller to load its nib — for example, to create the table view. For these tests, call

[rootVC view];

To test that a view controller is pushed, use a testing subclass of your actual view controller and override navigationController to return a mock object you can verify. For example,

@interface TestingMyViewController : MyViewController
@property(nonatomic, assign) UINavigationController *mockNavigationController;
@end

@implementation TestingMyViewController

@synthesize mockNavigationController;

- (UINavigationController *)navigationController
{
    return mockNavigationController;
}

@end

Then have your test create a TestingMyViewController and a mock. Set up the mock, and assign it to the testing subclass. Invoke the method you want to test, then verify that the mock navigation controller received the call you expected, with the argument you expected.