如何存根/注入视图控制器以在 iOS 5 运行时进行基于状态的测试?
Posted
技术标签:
【中文标题】如何存根/注入视图控制器以在 iOS 5 运行时进行基于状态的测试?【英文标题】:How to stub out / inject a view controller for state based testing on the iOS 5 runtime? 【发布时间】:2012-03-17 20:33:45 【问题描述】:我正在寻找一种“最佳实践”/“低测试摩擦”的方法来对我的基础 AppDelegate 类中的视图控制器进行基于状态的测试。目前,以下提供了一种简单的方法来存根我自己的 UIViewController(使用 ocmock),当它在类的方法中发生某些事情时。
-(FirstViewController *)getFirstViewController
if (self.viewController1)
return self.viewController1;
self.viewController1 = [[FirstViewController alloc] initWithNibName:@"FirstViewController" bundle:nil];
return self.viewController1;
我的第一个问题 - 这是一种有效的方法来存根/注入我自己的模拟视图控制器进行测试吗? (似乎效果很好,但我不确定这是否是专业人士今天进行基于状态的测试的方式)
我的下一个问题 - 像这样在内存中保留 1 个视图控制器副本是否有效(在应用程序的生命周期内只从头开始创建一次)?
**注意-我会依赖注入它,但我的 init 已经足够大,只需注入导航控制器和标签栏控制器,因此遗憾的是,这不是这个大型类的选项
【问题讨论】:
【参考方案1】:如果它是根视图控制器,则应将其设为应用委托的属性:
@interface MyAppDelegate : NSObject <UIApplicationDelegate>
@property(retain)FirstViewController *firstViewController;
@end
@implementation MyAppDelegate
@synthesize firstViewController;
...
@end
除非您正在测试的方法是您初始化firstViewController
的方法,否则您不需要任何类型的延迟加载方法。您只需在测试中获取应用委托,创建FirstViewController
的实例并将其分配给您的委托的属性,然后定义测试:
-(void)testSomething
MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
FirstViewController *firstViewController = [[FirstViewController alloc] init];
appDelegate.firstViewController = firstViewController;
// test some app delegate method
...
如果你想模拟你正在测试的控制器,你也可以这样做:
-(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];
【讨论】:
【参考方案2】:依赖注入不需要你通过 init 方法注入所有的依赖。首选是有原因的,但这是另一个讨论。
您可以简单地为您的类添加一个 -setFirstViewController: 方法。您将在测试中使用该方法来注入您的模拟。如果您不喜欢在您的应用中使用该方法,您可以使用测试代码中的类别添加该方法。
【讨论】:
公平点-但是如果我删除此方法并进行基本的 setter 注入,那么在我关于何时在 AppDelegate 中新建这些依赖项的问题的第二部分中,这将如何发挥作用? 您仍然可以在上面包含的 getter 中保留惰性初始化。只需确保在您的测试中,在第一次调用 getter 之前调用我建议的 setter。【参考方案3】:对于这种测试,我会让它像你一样,嗯,略有不同。
第一视图控制器的延迟加载被封装在一个属性中。
在.h文件中
@interface AppDelegate
FirstViewController *viewController1_;
然后
@property (nonatomic, readonly) FirstViewController viewController1;
在 .m 文件中
- (FirstViewController *)viewController1
if (!viewController1_)
viewController1_ = [[FirstViewController alloc] initWithNibName:@"FirstViewController" bundle:nil];
return viewController1_;
2nd- 如果我想注入一个模拟对象,我在我的测试代码中使用 KVC
[appDelegateUnderTest setValue:mockViewController forKey:@"viewController1_"];
问候,
【讨论】:
以上是关于如何存根/注入视图控制器以在 iOS 5 运行时进行基于状态的测试?的主要内容,如果未能解决你的问题,请参考以下文章
重写 iOS 应用程序以在 iOS 和 Mac 上运行——如何组织控制器代码?
如何从视图控制器发送数据以在 swift 5 中嵌入视图控制器?