views:

49

answers:

1

Unit Testing View Controllers seems to be very important part of iPhone Development (see this Article). This, however, requires initializing the Controllers from a Nib, which I found impossible to do properly in a logical test.

Loading Resources from a Bundle (see this Question) in a logical test works fine. Even loading Nibs is possible like this:

    UntitledViewController* controller = [[UntitledViewController alloc]initWithNibName:nil bundle:[NSBundle bundleForClass:[self class]]];

. It, however, only works as long as the nib only containts UIViews. Other views (I tried UITableView and UISwitch) result in otest failing with code 138.

Is it possible to test my Nibs using logical tests, and if so, how?

A: 

It depends on what you're trying to test. If you want to verify that your bindings are set up correctly, read Chris Hanson's article on unit testing your Cocoa interface. I personally think this is overkill, and leads to a proliferation of test code that's not very useful. But that's just my 2 cents.

If you actually try to interact with those interface elements in your test, you'll eventaully run into lots of otest errors, as you've found, particularly with UIActionSheets and UITableViews.

But your goal should be to unit test your controller's behavior, not the behavior of apple's UI objects. What I've found works best is to use OCMock to mock the UI elements and verify that the controller makes the expected calls on them. Here are a couple of examples:

  -(void)testClickingAButtonHidesAView {
     //setup
     id mockViewToHide = [OCMockObject mockForClass:[UIView class]];
     [[mockViewToHide expect] setHidden:YES];
     [controller setSomeView:mockViewToHide];

     // hideButtonClicked is an IBAction that is the hide button's target
     [controller hideButtonClicked];
     [mockViewToHide verify];
  }

  -(void)testActionSheetPublishClick {
     // ModelManager is the controller's dependency, which publishes Items
     id mockModelManager = [OCMockObject mockForClass:[ModelManager class]];
     [controller setModelManager:mockModelManager];

     // this doesn't really need to be mocked, but it's a convenient way to create
     // a reference you can validate in expect:
     id mockItem = [OCMockObject mockForClass:[Item class]];
     [controller setCurrentItem:mockItem];

     // when the user clicks "Publish" (the first button on the action sheet), 
     // the controller should publish the current item
     [[mockModelManager expect] publishItem:mockItem];

     // stub object so I know which action sheet was clicked
     id mockPublishActionSheet = [OCMockObject mockForClass:[UIActionSheet class]];
     [controller setPublishConfirmation:mockPublishActionSheet];

     // simulate action sheet click
     [controller actionSheet:mockPublishActionSheet didDismissWithButtonIndex:0];

     [mockModelManager verify];
  }
chrispix