Objective c 在UIViewController上设置只读navigationController属性以进行模拟
我已经使用OCMock创建了一个模拟UINavigationController。但是,我无法将其分配给UIViewController的navigationController属性,因为该属性是只读的Objective c 在UIViewController上设置只读navigationController属性以进行模拟,objective-c,cocoa-touch,unit-testing,ocmock,Objective C,Cocoa Touch,Unit Testing,Ocmock,我已经使用OCMock创建了一个模拟UINavigationController。但是,我无法将其分配给UIViewController的navigationController属性,因为该属性是只读的 id mockNavController = [OCMockObject mockForClass:[UINavigationController class]]; ... myViewController.navigationController = mockNavController; //
id mockNavController = [OCMockObject mockForClass:[UINavigationController class]];
...
myViewController.navigationController = mockNavController; // readonly!
《声明》的作者发现了一个解决方案,但忽略了分享它。我在测试中使用的一种技术是定义一个类别,将方法添加到主类中,以便访问内部属性。您可以尝试使用一个类别来合成setter,但您可能需要知道保存导航控制器指针的变量名。有几种可能的解决方案 您可以为navigationController调用私有setter,但这可能不存在,也不可能在所有情况下都可靠地工作 您可以按照Derek的建议创建一个类别,重新定义UIViewController上的navigationController属性。对navigationController属性的访问应该是安全的,但是如果UIViewController在任何地方直接访问支持ivar,并且您在类别中没有使用相同的ivar,那么您可能会看到意外的行为
您可以使用UINavigationController的部分模拟,如中所示。在这种情况下,您的测试并不像您希望的那样孤立,但至少UIViewController超类和UINavigationController的私有行为应该保持不变。无需创建允许您设置navigationController属性的变体,因为您可以模拟返回它的访问器。我是这样做的:
-(void)testTappingSettingsButtonShouldDisplaySettings {
MyController *myController = [[MyController alloc] init];
// expect the nav controller to push a settings controller
id mockNavigationController = [OCMockObject mockForClass:[UINavigationController class]];
[[mockNavigationController expect] pushViewController:[OCMArg any] animated:YES];
// set up myController to return the mocked navigation controller
id mockController = [OCMockObject partialMockForObject:myController];
[[[mockController expect] andReturn:mockNavigationController] navigationController];
[myController settingsButtonTapped];
[mockNavigationController verify];
[mockController verify];
[myController release];
}
这是一个非常晚的响应,但对于后代来说,我刚刚发现另一种方法是采用基于状态的方法,并将测试中的视图控制器粘贴到真正的导航控制器中。然后,您可以戳一下视图控制器,通过检查导航控制器的状态来测试它对导航堆栈的作用。下面是一个例子:
it(@"displays the station chooser when you tap the 'Choose station' button", ^{
// Given
LaunchViewController *launchViewController = [LaunchViewController newWithNearestStationLocator:nil];
[launchViewController view];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:launchViewController];
// When
[[launchViewController chooseStationBtn] sendActionsForControlEvents:UIControlEventTouchUpInside];
// Then
[[theValue(navController.viewControllers.count) should] equal:theValue(2)];
[[NSStringFromClass(navController.visibleViewController.class) should] equal:@"StationsViewController"];
});
谢谢,这是一个有趣的解决方案。