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

发布于 2024-09-10 15:47:47 字数 571 浏览 3 评论 0原文

我正在编写单元测试。我无法测试一个函数,因为它调用 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]; 

您会在我的位置做什么?

I'm writing unit tests. And I cannot test one function, because it calls keyWindow

UIWindow* window = [UIApplication sharedApplication].keyWindow;

And keyWindow returns nil (I don't have any window).
But I need to return anything, but nil.

I used category to manually set keyWindow value, but this didn't work

@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]; 

What would you do in my place?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

寂寞笑我太脆弱 2024-09-17 15:47:47

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

一种选择是根本不对此进行测试。大多数时候,您与主窗口的交互都相对简单,并且此代码的​​测试正在测试 UIKit 框架,就像测试您自己的代码一样。这是一个风格上的决定,取决于您如何构建代码、您是否愿意让这个小区域未经测试(或者更恰当地说,未测试自动化)以及编写测试的难度。

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

Apple doesn't make it easy to test your code when you start getting close to the framework classes. I've found UIApplication particularly difficult due to its singleton nature, and the fact that the framework creates the instance for you during setup. Solutions to problems like this usually depend on how you're testing; for example, do you have a valid UIApplication object in your tests? If you're using OCUnit then [UIApplication sharedApplication] may itself return nil, unless you've set up your test target to run in your application bundle (I've never managed to get this to work, honestly).

One option is to simply not test this. Most of the time your interactions with the main window are relatively simple, and tests of this code are testing the UIKit framework as much as your own code. This is a stylistic decision that depends on how you've structured your code, how comfortable you are leaving this small area untested (or, more appropriately, un-test-automated), and how difficult it will be to write the tests.

If you have code that relates to the UIWindow object that you decide you do need to test, I would suggest you encapsulate the functionality in a way that you can then control under test. You could do this by creating a subclass of UIApplication for your app which returns the UIWindow object from a custom method. Use this method in your code, rather than accessing the window property directly, and in your tests override this method to return whatever you like.

风柔一江水 2024-09-17 15:47:47

我能够模拟委托对象并通过调整应用程序委托设置器来验证某些期望,例如 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
}

}

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

I was able to mock the delegate object and verify certain expectations such as applicationDidBecomeActive by swizzling the application delegate setter.

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

Instead:

@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
}

}

Note this example is quite custom to what I needed to test, you'll need to think about what you want to mock and if its even possible.

日记撕了你也走了 2024-09-17 15:47:47

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

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

You can create the new window object:

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文