Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

xctest - how to test if a new view loads on a button press

Just started xcode 5 and xctest. How do I test that a view loads on button press. I have programatically added method that gets called when the rightBarButtonItem is clicked

action:@selector(onSettingsButton)

and in onSettingsButton

-(void) onSettingsButton{
    SettingsViewController *svc = [[SettingsViewController alloc] init];
    [self.navigationController pushViewController:svc animated:YES];
}

How to write xctest to ensure SettingsViewController brings up the Settings view? Thank you.

like image 778
U-L Avatar asked Dec 09 '13 02:12

U-L


2 Answers

You need an interaction test — that is, a test that checks interactions between objects. In this case, you want to test that -pushViewController:animated: is called on the navigation controller with a SettingsViewController. So we want to put a mock object into self.navigationController which we can ask, "Were you called as expected?"

I'll assume a simple name for the class: MyView.

The way I'd do this by hand is to Subclass and Override navigationController. So in my test code, I'd do something like this:

@interface TestableMyView : MyView
@property (nonatomic, strong) id mockNavigationController;
@end

@implementation TestableMyView

- (UINavigationController *)navigationController
{
    return mockNavigationController;
}

@end

Now instead of creating a MyView, the test will create a TestableMyView and set its mockNavigationController property.

This mock can be anything, as long as it responds to -pushViewController:animated: and records the arguments. Here's a simple example, by hand:

@interface MockNavigationController : NSObject
@property (nonatomic) int pushViewControllerCount;
@property (nonatomic, strong) UIViewController *pushedViewController;
@property (nonatomic) BOOL wasPushViewControllerAnimated;
@end

@implementation MockNavigationController

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    self.pushViewControllerCount += 1;
    self.pushedViewController = viewController;
    self.wasPushViewControllerAnimated = animated;
}

@end

Finally, here's a test:

- (void)testOnSettingsButton_ShouldPushSettingsViewController
{
    // given
    MockNavigationController *mockNav = [[MockNavigationController alloc] init];
    TestableMyView *sut = [[TestableMyView alloc] init];
    sut.mockNavigationController = mockNav;

    // when
    [sut onSettingsButton];

    // then
    XCTAssertEquals(1, mockNav.pushViewControllerCount);
    XCTAssertTrue([mockNav.pushedViewController isKindOfClass:[SettingsViewController class]]);
}

These things can be simplified by using mock object frameworks such as OCMock, OCMockito, or Kiwi's mocking. But I think it helps to start by hand first, so that you understand the concepts. Then choose the tools that help. And if you know how to do it by hand, you'll never say, "Mocking framework X doesn't do what I need! I'm stuck!"

like image 115
Jon Reid Avatar answered Sep 20 '22 21:09

Jon Reid


Found one way. Maybe there are others ..

- (void)testSettingsViewShowsWhenSettingsButtonIsClicked{
    [self.tipViewController onSettingsButton];
    id temp = self.tipViewController.navigationController.visibleViewController;
    XCTAssertEqual([temp class], [SettingsViewController class], @"Current controller should be Settings view controller");
}

First call the onSettingsButton, which is the same as clicking the button, but not really. Maybe it's okay for this simple test case? How to simulate the actual press?

Then get the current view from the tipviewcontoller which is the rootview of the app and check that it is a SettingsViewController.

like image 28
U-L Avatar answered Sep 17 '22 21:09

U-L