【问题标题】:How to stub out / inject a view controller for state based testing on the iOS 5 runtime?如何存根/注入视图控制器以在 iOS 5 运行时进行基于状态的测试?
【发布时间】: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 已经足够大,只需注入导航控制器和标签栏控制器,因此遗憾的是,这不是这个大型类的选项

【问题讨论】:

    标签: objective-c ios unit-testing ocmock


    【解决方案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_"];
      

      问候,

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-14
        • 1970-01-01
        • 2014-05-03
        • 1970-01-01
        • 2015-10-13
        相关资源
        最近更新 更多