如何模拟UIApplication的属性/内部​​价值?

时间:2010-07-16 14:38:59

标签: objective-c unit-testing mocking uiapplication

我正在编写单元测试。我无法测试一个函数,因为它调用keyWindow

UIWindow* window = [UIApplication sharedApplication].keyWindow;

keyWindow返回nil(我没有任何窗口)。 但我需要退货,但没有。

我使用类别手动设置keyWindow值,但这不起作用

@interface UIApplication(UnitTest)
- (id)getKeyWindow;
@end

@implementation UIApplication(UnitTest)
- (id)getKeyWindow
{
    return [self keyWindow];
}
@end

// compiler error: lvalue required as left operand of assignment
[[UIApplication sharedApplication] getKeyWindow] = [[UIWindow alloc] init]; 

你会在我的位置做什么?

3 个答案:

答案 0 :(得分:3)

当您开始接近框架类时,Apple无法轻松测试代码。我发现UIApplication由于其单例性质而特别困难,并且框架在安装过程中为您创建了实例。这类问题的解决方案通常取决于您的测试方式;例如,您的测试中是否有有效的UIApplication对象?如果您正在使用OCUnit,那么[UIApplication sharedApplication]本身可能会返回nil,除非您已将测试目标设置为在应用程序包中运行(我从未设法让它工作,老实说)。

一种选择是不测试这个。大多数情况下,您与主窗口的交互相对简单,并且此代码的​​测试正在测试UIKit框架和您自己的代码一样多。这是一个风格决定,取决于您如何构建代码,离开这个小区域未经测试(或者更合适的是,未测试自动化)的舒适程度,以及编写测试的难度。 / p>

如果您有与您确定需要测试的UIWindow对象相关的代码,我建议您以一种可以在测试中控制的方式封装功能。您可以通过为您的应用程序创建UIApplication的子类来执行此操作,该子类从自定义方法返回UIWindow对象。在代码中使用此方法,而不是直接访问窗口属性,并在测试中重写此方法以返回您喜欢的任何内容。

答案 1 :(得分:0)

我能够通过调整应用程序委托setter来模拟委托对象并验证某些期望,例如applicationDidBecomeActive。

id<UIApplicationDelegate> delegate = [MyCustomDelegate alloc] init];
UIApplication *app = [UIApplication alloc] init];
app.delegate = delegate;
//results in "There can only be one UIApplication" error

相反:

@implementation AppDelegateTests {

- (void)testAppDelegate {
     //perform swizzle on [UIApplication class] and method @selector(setDelegate:) with
     //[self class] and at the bottom @selector(swizzledSetDelegate:)

     id<UIApplicationDelegate> delegate = [MyCustomDelegate alloc] init];

     //Here's the magic, this line actually calls [UIApplication setDelegate] and not the swizzledSetDelegate method below. 
     //If you don't understand this, look up how swizzling works and hopefully you can wrap your head around it. The logic is quite mind-bendy.
     [self swizzledSetDelegate:delegate];

     //here set up your mock on the delegate and verify state of things
}

- (void)swizzledSetDelegate:(id<UIApplicationDelegate>)delegate {
     //do nothing
}

}

请注意,此示例非常适合我需要测试的内容,您需要考虑要模拟的内容以及它是否可能。

答案 2 :(得分:0)

您可以创建新的窗口对象:

UIWindow * window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];